Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
authorDavid S. Miller <davem@davemloft.net>
Wed, 23 May 2018 20:37:11 +0000 (16:37 -0400)
committerDavid S. Miller <davem@davemloft.net>
Wed, 23 May 2018 20:37:11 +0000 (16:37 -0400)
Pablo Neira Ayuso says:

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

The following patchset contains Netfilter updates for your net-next
tree, they are:

1) Remove obsolete nf_log tracing from nf_tables, from Florian Westphal.

2) Add support for map lookups to numgen, random and hash expressions,
   from Laura Garcia.

3) Allow to register nat hooks for iptables and nftables at the same
   time. Patchset from Florian Westpha.

4) Timeout support for rbtree sets.

5) ip6_rpfilter works needs interface for link-local addresses, from
   Vincent Bernat.

6) Add nf_ct_hook and nf_nat_hook structures and use them.

7) Do not drop packets on packets raceing to insert conntrack entries
   into hashes, this is particularly a problem in nfqueue setups.

8) Address fallout from xt_osf separation to nf_osf, patches
   from Florian Westphal and Fernando Mancera.

9) Remove reference to struct nft_af_info, which doesn't exist anymore.
   From Taehee Yoo.

This batch comes with is a conflict between 25fd386e0bc0 ("netfilter:
core: add missing __rcu annotation") in your tree and 2c205dd3981f
("netfilter: add struct nf_nat_hook and use it") coming in this batch.
This conflict can be solved by leaving the __rcu tag on
__netfilter_net_init() - added by 25fd386e0bc0 - and remove all code
related to nf_nat_decode_session_hook - which is gone after
2c205dd3981f, as described by:

diff --cc net/netfilter/core.c
index e0ae4aae96f5,206fb2c4c319..168af54db975
--- a/net/netfilter/core.c
+++ b/net/netfilter/core.c
@@@ -611,7 -580,13 +611,8 @@@ const struct nf_conntrack_zone nf_ct_zo
  EXPORT_SYMBOL_GPL(nf_ct_zone_dflt);
  #endif /* CONFIG_NF_CONNTRACK */

- static void __net_init __netfilter_net_init(struct nf_hook_entries **e, int max)
 -#ifdef CONFIG_NF_NAT_NEEDED
 -void (*nf_nat_decode_session_hook)(struct sk_buff *, struct flowi *);
 -EXPORT_SYMBOL(nf_nat_decode_session_hook);
 -#endif
 -
+ static void __net_init
+ __netfilter_net_init(struct nf_hook_entries __rcu **e, int max)
  {
   int h;

I can also merge your net-next tree into nf-next, solve the conflict and
resend the pull request if you prefer so.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
969 files changed:
Documentation/ABI/testing/sysfs-class-cxl
Documentation/bpf/README.rst [new file with mode: 0644]
Documentation/bpf/bpf_design_QA.rst [new file with mode: 0644]
Documentation/bpf/bpf_design_QA.txt [deleted file]
Documentation/bpf/bpf_devel_QA.rst [new file with mode: 0644]
Documentation/bpf/bpf_devel_QA.txt [deleted file]
Documentation/devicetree/bindings/net/dsa/qca8k.txt
Documentation/devicetree/bindings/net/dwmac-sun8i.txt
Documentation/devicetree/bindings/net/marvell-pp2.txt
Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
Documentation/devicetree/bindings/net/mscc-miim.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/mscc-ocelot.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/qualcomm-bluetooth.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/sff,sfp.txt
Documentation/devicetree/bindings/net/sh_eth.txt
Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt
Documentation/networking/filter.txt
Documentation/networking/ip-sysctl.txt
Documentation/virtual/kvm/cpuid.txt
MAINTAINERS
Makefile
arch/Kconfig
arch/arm/boot/compressed/Makefile
arch/arm/boot/compressed/head.S
arch/arm/boot/dts/bcm-cygnus.dtsi
arch/arm/boot/dts/da850-lcdk.dts
arch/arm/boot/dts/da850.dtsi
arch/arm/boot/dts/dm8148-evm.dts
arch/arm/boot/dts/dm8148-t410.dts
arch/arm/boot/dts/dm8168-evm.dts
arch/arm/boot/dts/dra62x-j5eco-evm.dts
arch/arm/boot/dts/imx51-zii-rdu1.dts
arch/arm/boot/dts/imx7s.dtsi
arch/arm/boot/dts/logicpd-som-lv.dtsi
arch/arm/boot/dts/r8a7790-lager.dts
arch/arm/boot/dts/r8a7790.dtsi
arch/arm/boot/dts/r8a7791-koelsch.dts
arch/arm/boot/dts/r8a7791-porter.dts
arch/arm/boot/dts/r8a7791.dtsi
arch/arm/boot/dts/r8a7793-gose.dts
arch/arm/boot/dts/r8a7793.dtsi
arch/arm/boot/dts/tegra20.dtsi
arch/arm/include/asm/assembler.h
arch/arm/include/asm/kvm_mmu.h
arch/arm/include/uapi/asm/siginfo.h [deleted file]
arch/arm/kernel/machine_kexec.c
arch/arm/kernel/traps.c
arch/arm/lib/getuser.S
arch/arm/mach-davinci/board-da830-evm.c
arch/arm/mach-davinci/board-da850-evm.c
arch/arm/mach-davinci/board-dm355-evm.c
arch/arm/mach-davinci/board-dm644x-evm.c
arch/arm/mach-davinci/board-dm646x-evm.c
arch/arm/mach-davinci/board-omapl138-hawk.c
arch/arm/mach-davinci/dm646x.c
arch/arm/mach-keystone/pm_domain.c
arch/arm/mach-omap1/ams-delta-fiq.c
arch/arm/mach-omap2/powerdomain.c
arch/arm/net/bpf_jit_32.c
arch/arm/probes/kprobes/opt-arm.c
arch/arm/vfp/vfpmodule.c
arch/arm64/boot/dts/exynos/exynos5433.dtsi
arch/arm64/boot/dts/marvell/armada-cp110.dtsi
arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
arch/arm64/boot/dts/qcom/apq8096-db820c-pins.dtsi
arch/arm64/boot/dts/qcom/apq8096-db820c-pmic-pins.dtsi
arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi
arch/arm64/boot/dts/qcom/msm8996.dtsi
arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi
arch/arm64/boot/dts/socionext/uniphier-ld20-ref.dts
arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi
arch/arm64/boot/dts/socionext/uniphier-pxs3.dtsi
arch/arm64/include/asm/kvm_mmu.h
arch/arm64/net/bpf_jit_comp.c
arch/mips/boot/compressed/uart-16550.c
arch/mips/boot/dts/xilfpga/Makefile
arch/mips/generic/Platform
arch/mips/kernel/ptrace.c
arch/mips/kernel/ptrace32.c
arch/mips/kvm/mips.c
arch/mips/mm/c-r4k.c
arch/mips/net/ebpf_jit.c
arch/parisc/kernel/drivers.c
arch/parisc/kernel/smp.c
arch/powerpc/platforms/powernv/opal-nvram.c
arch/s390/configs/debug_defconfig
arch/s390/configs/performance_defconfig
arch/s390/crypto/crc32be-vx.S
arch/s390/crypto/crc32le-vx.S
arch/s390/include/asm/nospec-insn.h [new file with mode: 0644]
arch/s390/include/asm/purgatory.h
arch/s390/kernel/Makefile
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/base.S
arch/s390/kernel/entry.S
arch/s390/kernel/irq.c
arch/s390/kernel/mcount.S
arch/s390/kernel/nospec-branch.c
arch/s390/kernel/nospec-sysfs.c [new file with mode: 0644]
arch/s390/kernel/perf_cpum_sf.c
arch/s390/kernel/reipl.S
arch/s390/kernel/swsusp.S
arch/s390/lib/mem.S
arch/s390/net/bpf_jit_comp.c
arch/sparc/net/bpf_jit_comp_64.c
arch/x86/boot/compressed/eboot.c
arch/x86/boot/compressed/head_64.S
arch/x86/boot/compressed/pgtable_64.c
arch/x86/entry/vdso/vdso32/vdso-fakesections.c [deleted file]
arch/x86/events/core.c
arch/x86/events/intel/cstate.c
arch/x86/events/msr.c
arch/x86/include/asm/cpufeature.h
arch/x86/include/asm/insn.h
arch/x86/include/asm/mmu_context.h
arch/x86/include/asm/nospec-branch.h
arch/x86/include/asm/pkeys.h
arch/x86/include/uapi/asm/kvm_para.h
arch/x86/kernel/amd_nb.c
arch/x86/kernel/apic/x2apic_cluster.c
arch/x86/kernel/cpu/mcheck/mce_amd.c
arch/x86/kernel/head64.c
arch/x86/kernel/kprobes/core.c
arch/x86/kernel/kvm.c
arch/x86/kernel/machine_kexec_32.c
arch/x86/kernel/machine_kexec_64.c
arch/x86/kernel/process_64.c
arch/x86/kernel/uprobes.c
arch/x86/kvm/hyperv.c
arch/x86/kvm/vmx.c
arch/x86/kvm/x86.c
arch/x86/mm/pkeys.c
arch/x86/xen/mmu.c
arch/x86/xen/mmu_pv.c
drivers/acpi/acpica/acnamesp.h
drivers/acpi/acpica/exconfig.c
drivers/acpi/acpica/nsinit.c
drivers/bluetooth/Kconfig
drivers/bluetooth/btbcm.c
drivers/bluetooth/btbcm.h
drivers/bluetooth/btqca.c
drivers/bluetooth/btqca.h
drivers/bluetooth/btqcomsmd.c
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_bcm.c
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_qca.c
drivers/clk/Kconfig
drivers/clk/imx/clk-imx6ul.c
drivers/cpufreq/Kconfig.arm
drivers/dma/qcom/bam_dma.c
drivers/firmware/arm_scmi/driver.c
drivers/firmware/efi/libstub/arm64-stub.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_dumb_buffers.c
drivers/gpu/drm/drm_file.c
drivers/gpu/drm/i915/i915_gem_userptr.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_engine_cs.c
drivers/gpu/drm/i915/intel_lrc.c
drivers/gpu/drm/vc4/vc4_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
drivers/hwmon/Kconfig
drivers/hwmon/k10temp.c
drivers/i2c/busses/i2c-designware-master.c
drivers/i2c/busses/i2c-pmcmsp.c
drivers/i2c/busses/i2c-viperboard.c
drivers/i2c/i2c-core-acpi.c
drivers/infiniband/hw/mlx5/cq.c
drivers/md/bcache/debug.c
drivers/misc/cxl/cxl.h
drivers/misc/cxl/pci.c
drivers/misc/cxl/sysfs.c
drivers/misc/eeprom/at24.c
drivers/mtd/nand/raw/marvell_nand.c
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_options.c
drivers/net/dsa/bcm_sf2.c
drivers/net/dsa/bcm_sf2_cfp.c
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/mv88e6xxx/chip.h
drivers/net/dsa/mv88e6xxx/global1.c
drivers/net/dsa/mv88e6xxx/global1.h
drivers/net/dsa/mv88e6xxx/global2.c
drivers/net/dsa/qca8k.c
drivers/net/dsa/qca8k.h
drivers/net/ethernet/3com/3c59x.c
drivers/net/ethernet/8390/ne.c
drivers/net/ethernet/Kconfig
drivers/net/ethernet/Makefile
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
drivers/net/ethernet/amd/xgbe/xgbe-main.c
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
drivers/net/ethernet/amd/xgbe/xgbe-pci.c
drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
drivers/net/ethernet/amd/xgbe/xgbe.h
drivers/net/ethernet/cadence/macb_main.c
drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h
drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h
drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
drivers/net/ethernet/chelsio/cxgb4/l2t.c
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h
drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
drivers/net/ethernet/ethoc.c
drivers/net/ethernet/freescale/Kconfig
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/hisilicon/hns3/hnae3.c
drivers/net/ethernet/hisilicon/hns3/hnae3.h
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h
drivers/net/ethernet/ibm/ibmvnic.c
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_ptp.c
drivers/net/ethernet/intel/i40evf/i40evf.h
drivers/net/ethernet/intel/i40evf/i40evf_client.h
drivers/net/ethernet/intel/i40evf/i40evf_main.c
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
drivers/net/ethernet/intel/ice/ice_nvm.c
drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_model.h
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/marvell/Kconfig
drivers/net/ethernet/marvell/mvmdio.c
drivers/net/ethernet/marvell/mvpp2.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h
drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
drivers/net/ethernet/mellanox/mlx5/core/en_rep.h
drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
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/en_txrx.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/fs_cmd.c
drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
drivers/net/ethernet/mellanox/mlx5/core/vport.c
drivers/net/ethernet/mellanox/mlxsw/core.c
drivers/net/ethernet/mellanox/mlxsw/core.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
drivers/net/ethernet/mellanox/mlxsw/switchx2.c
drivers/net/ethernet/mscc/Kconfig [new file with mode: 0644]
drivers/net/ethernet/mscc/Makefile [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot.c [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot.h [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_ana.h [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_board.c [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_dev.h [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_dev_gmii.h [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_hsio.h [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_io.c [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_qs.h [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_qsys.h [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_regs.c [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_rew.h [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_sys.h [new file with mode: 0644]
drivers/net/ethernet/neterion/vxge/vxge-config.c
drivers/net/ethernet/neterion/vxge/vxge-config.h
drivers/net/ethernet/neterion/vxge/vxge-ethtool.c
drivers/net/ethernet/neterion/vxge/vxge-main.c
drivers/net/ethernet/netronome/Kconfig
drivers/net/ethernet/netronome/nfp/Makefile
drivers/net/ethernet/netronome/nfp/abm/ctrl.c [new file with mode: 0644]
drivers/net/ethernet/netronome/nfp/abm/main.c [new file with mode: 0644]
drivers/net/ethernet/netronome/nfp/abm/main.h [new file with mode: 0644]
drivers/net/ethernet/netronome/nfp/bpf/fw.h
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/verifier.c
drivers/net/ethernet/netronome/nfp/flower/main.c
drivers/net/ethernet/netronome/nfp/nfp_abi.h [new file with mode: 0644]
drivers/net/ethernet/netronome/nfp/nfp_app.c
drivers/net/ethernet/netronome/nfp/nfp_app.h
drivers/net/ethernet/netronome/nfp/nfp_app_nic.c
drivers/net/ethernet/netronome/nfp/nfp_asm.h
drivers/net/ethernet/netronome/nfp/nfp_devlink.c
drivers/net/ethernet/netronome/nfp/nfp_main.c
drivers/net/ethernet/netronome/nfp/nfp_main.h
drivers/net/ethernet/netronome/nfp/nfp_net.h
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
drivers/net/ethernet/netronome/nfp/nfp_net_main.c
drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
drivers/net/ethernet/netronome/nfp/nfp_net_repr.h
drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
drivers/net/ethernet/netronome/nfp/nfp_port.c
drivers/net/ethernet/netronome/nfp/nfp_port.h
drivers/net/ethernet/netronome/nfp/nfp_shared_buf.c [new file with mode: 0644]
drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c
drivers/net/ethernet/qlogic/qed/Makefile
drivers/net/ethernet/qlogic/qed/qed.h
drivers/net/ethernet/qlogic/qed/qed_hsi.h
drivers/net/ethernet/qlogic/qed/qed_ll2.c
drivers/net/ethernet/qlogic/qed/qed_main.c
drivers/net/ethernet/qlogic/qed/qed_mcp.c
drivers/net/ethernet/qlogic/qed/qed_mcp.h
drivers/net/ethernet/qlogic/qed/qed_mng_tlv.c [new file with mode: 0644]
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/qualcomm/emac/emac-mac.c
drivers/net/ethernet/qualcomm/emac/emac-sgmii.c
drivers/net/ethernet/qualcomm/emac/emac-sgmii.h
drivers/net/ethernet/qualcomm/emac/emac.c
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
drivers/net/ethernet/realtek/r8169.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/renesas/sh_eth.h
drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
drivers/net/ethernet/stmicro/stmmac/enh_desc.c
drivers/net/ethernet/stmicro/stmmac/hwif.c
drivers/net/ethernet/stmicro/stmmac/hwif.h
drivers/net/ethernet/stmicro/stmmac/norm_desc.c
drivers/net/ethernet/stmicro/stmmac/stmmac.h
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
drivers/net/ethernet/ti/Kconfig
drivers/net/ethernet/ti/cpsw-phy-sel.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpts.c
drivers/net/ethernet/ti/davinci_cpdma.c
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/ethernet/ti/davinci_mdio.c
drivers/net/hippi/rrunner.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/ipvlan/ipvlan_main.c
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/mdio-gpio.c
drivers/net/phy/mdio-mscc-miim.c [new file with mode: 0644]
drivers/net/phy/micrel.c
drivers/net/phy/phylink.c
drivers/net/phy/sfp.c
drivers/net/team/team.c
drivers/net/tun.c
drivers/net/usb/lan78xx.c
drivers/net/vmxnet3/vmxnet3_drv.c
drivers/net/vmxnet3/vmxnet3_ethtool.c
drivers/net/vmxnet3/vmxnet3_int.h
drivers/net/wireless/ath/ath10k/Kconfig
drivers/net/wireless/ath/ath10k/Makefile
drivers/net/wireless/ath/ath10k/ce.c
drivers/net/wireless/ath/ath10k/ce.h
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/hif.h
drivers/net/wireless/ath/ath10k/htc.c
drivers/net/wireless/ath/ath10k/htc.h
drivers/net/wireless/ath/ath10k/htt.c
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/htt_tx.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/pci.c
drivers/net/wireless/ath/ath10k/sdio.c
drivers/net/wireless/ath/ath10k/snoc.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/snoc.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/txrx.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/debug.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath9k/dfs.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/wcn36xx/dxe.c
drivers/net/wireless/ath/wcn36xx/dxe.h
drivers/net/wireless/ath/wcn36xx/hal.h
drivers/net/wireless/ath/wcn36xx/main.c
drivers/net/wireless/ath/wcn36xx/smd.c
drivers/net/wireless/ath/wcn36xx/smd.h
drivers/net/wireless/ath/wcn36xx/txrx.c
drivers/net/wireless/ath/wcn36xx/wcn36xx.h
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/broadcom/b43/dma.c
drivers/net/wireless/broadcom/b43legacy/dma.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c
drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
drivers/net/wireless/intel/ipw2x00/ipw2100.c
drivers/net/wireless/intel/ipw2x00/ipw2100.h
drivers/net/wireless/intel/ipw2x00/ipw2200.c
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/5000.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/api/datapath.h
drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
drivers/net/wireless/intel/iwlwifi/fw/api/rs.h
drivers/net/wireless/intel/iwlwifi/fw/api/txq.h
drivers/net/wireless/intel/iwlwifi/fw/dbg.h
drivers/net/wireless/intel/iwlwifi/fw/debugfs.c
drivers/net/wireless/intel/iwlwifi/fw/debugfs.h
drivers/net/wireless/intel/iwlwifi/fw/file.h
drivers/net/wireless/intel/iwlwifi/fw/img.h
drivers/net/wireless/intel/iwlwifi/fw/nvm.c [deleted file]
drivers/net/wireless/intel/iwlwifi/fw/paging.c
drivers/net/wireless/intel/iwlwifi/fw/runtime.h
drivers/net/wireless/intel/iwlwifi/iwl-config.h
drivers/net/wireless/intel/iwlwifi/iwl-csr.h
drivers/net/wireless/intel/iwlwifi/iwl-drv.c
drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c
drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c
drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
drivers/net/wireless/intel/iwlwifi/iwl-trans.h
drivers/net/wireless/intel/iwlwifi/mvm/coex.c
drivers/net/wireless/intel/iwlwifi/mvm/constants.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/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/rs.c
drivers/net/wireless/intel/iwlwifi/mvm/rs.h
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.c
drivers/net/wireless/intel/iwlwifi/pcie/internal.h
drivers/net/wireless/intel/iwlwifi/pcie/rx.c
drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c
drivers/net/wireless/intel/iwlwifi/pcie/trans.c
drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
drivers/net/wireless/intel/iwlwifi/pcie/tx.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/marvell/mwifiex/cfg80211.c
drivers/net/wireless/marvell/mwifiex/cmdevt.c
drivers/net/wireless/marvell/mwifiex/ie.c
drivers/net/wireless/marvell/mwifiex/main.c
drivers/net/wireless/marvell/mwifiex/main.h
drivers/net/wireless/marvell/mwifiex/pcie.c
drivers/net/wireless/marvell/mwifiex/uap_event.c
drivers/net/wireless/mediatek/mt76/agg-rx.c
drivers/net/wireless/mediatek/mt76/mac80211.c
drivers/net/wireless/mediatek/mt76/mt76.h
drivers/net/wireless/mediatek/mt76/mt76x2.h
drivers/net/wireless/mediatek/mt76/mt76x2_dma.h
drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h
drivers/net/wireless/mediatek/mt76/mt76x2_init.c
drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
drivers/net/wireless/mediatek/mt76/mt76x2_mac.h
drivers/net/wireless/mediatek/mt76/mt76x2_main.c
drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
drivers/net/wireless/mediatek/mt76/tx.c
drivers/net/wireless/mediatek/mt7601u/mac.c
drivers/net/wireless/mediatek/mt7601u/main.c
drivers/net/wireless/mediatek/mt7601u/mt7601u.h
drivers/net/wireless/mediatek/mt7601u/phy.c
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
drivers/net/wireless/quantenna/qtnfmac/core.c
drivers/net/wireless/quantenna/qtnfmac/event.c
drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
drivers/net/wireless/quantenna/qtnfmac/qlink.h
drivers/net/wireless/ralink/rt2x00/rt2800.h
drivers/net/wireless/ralink/rt2x00/rt2800lib.c
drivers/net/wireless/ralink/rt2x00/rt2800lib.h
drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
drivers/net/wireless/ralink/rt2x00/rt2800pci.c
drivers/net/wireless/ralink/rt2x00/rt2800soc.c
drivers/net/wireless/ralink/rt2x00/rt2800usb.c
drivers/net/wireless/ralink/rt2x00/rt2x00.h
drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c
drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c
drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
drivers/net/wireless/realtek/rtlwifi/wifi.h
drivers/net/wireless/rsi/rsi_91x_coex.c
drivers/net/wireless/rsi/rsi_91x_core.c
drivers/net/wireless/rsi/rsi_91x_hal.c
drivers/net/wireless/rsi/rsi_91x_mac80211.c
drivers/net/wireless/rsi/rsi_91x_mgmt.c
drivers/net/wireless/rsi/rsi_91x_sdio.c
drivers/net/wireless/rsi/rsi_91x_usb.c
drivers/net/wireless/rsi/rsi_boot_params.h
drivers/net/wireless/rsi/rsi_hal.h
drivers/net/wireless/rsi/rsi_main.h
drivers/net/wireless/rsi/rsi_mgmt.h
drivers/net/wireless/rsi/rsi_sdio.h
drivers/net/wireless/rsi/rsi_usb.h
drivers/net/wireless/st/cw1200/txrx.c
drivers/net/wireless/ti/wlcore/sdio.c
drivers/nvme/host/core.c
drivers/nvme/host/nvme.h
drivers/nvme/host/pci.c
drivers/of/of_mdio.c
drivers/parisc/ccio-dma.c
drivers/phy/marvell/phy-mvebu-cp110-comphy.c
drivers/platform/x86/Kconfig
drivers/reset/reset-uniphier.c
drivers/s390/cio/qdio_setup.c
drivers/s390/cio/vfio_ccw_cp.c
drivers/scsi/aacraid/commsup.c
drivers/scsi/csiostor/csio_hw.c
drivers/scsi/qedf/qedf.h
drivers/scsi/qedf/qedf_debugfs.c
drivers/scsi/qedf/qedf_io.c
drivers/scsi/qedf/qedf_main.c
drivers/scsi/qedi/qedi.h
drivers/scsi/qedi/qedi_iscsi.h
drivers/scsi/qedi/qedi_main.c
drivers/scsi/vmw_pvscsi.c
drivers/spi/spi-bcm-qspi.c
drivers/spi/spi-bcm2835aux.c
drivers/spi/spi-cadence.c
drivers/spi/spi-imx.c
drivers/spi/spi-pxa2xx.h
drivers/spi/spi-sh-msiof.c
drivers/tee/tee_core.c
drivers/tee/tee_shm.c
drivers/thermal/int340x_thermal/int3403_thermal.c
drivers/thermal/samsung/exynos_tmu.c
drivers/usb/host/xhci-hub.c
drivers/usb/musb/musb_host.c
drivers/usb/musb/musb_host.h
drivers/usb/musb/musb_virthub.c
drivers/usb/usbip/stub.h
drivers/usb/usbip/stub_dev.c
drivers/usb/usbip/stub_main.c
fs/afs/addr_list.c
fs/afs/callback.c
fs/afs/cmservice.c
fs/afs/dir.c
fs/afs/file.c
fs/afs/flock.c
fs/afs/fsclient.c
fs/afs/inode.c
fs/afs/internal.h
fs/afs/rotate.c
fs/afs/rxrpc.c
fs/afs/security.c
fs/afs/server.c
fs/afs/server_list.c
fs/afs/super.c
fs/afs/write.c
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/inode.c
fs/btrfs/props.c
fs/btrfs/tree-log.c
fs/btrfs/volumes.c
fs/cifs/cifsfs.c
fs/cifs/connect.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/exec.c
fs/hfsplus/super.c
fs/ocfs2/refcounttree.c
fs/proc/Kconfig
fs/proc/base.c
fs/proc/kcore.c
fs/proc/vmcore.c
include/linux/avf/virtchnl.h
include/linux/binfmts.h
include/linux/bpf.h
include/linux/bpf_types.h
include/linux/bpf_verifier.h
include/linux/bpfilter.h [new file with mode: 0644]
include/linux/btf.h
include/linux/crash_dump.h
include/linux/efi.h
include/linux/filter.h
include/linux/kcore.h
include/linux/kthread.h
include/linux/kvm_host.h
include/linux/mlx5/driver.h
include/linux/mlx5/fs.h
include/linux/mlx5/mlx5_ifc.h
include/linux/mm.h
include/linux/mmc/sdio_ids.h
include/linux/mtd/map.h
include/linux/mtd/rawnand.h
include/linux/netdev_features.h
include/linux/oom.h
include/linux/percpu-rwsem.h
include/linux/phy/phy.h
include/linux/platform_data/b53.h
include/linux/platform_data/mv88e6xxx.h [new file with mode: 0644]
include/linux/qed/qed_if.h
include/linux/rbtree_augmented.h
include/linux/rbtree_latch.h
include/linux/rwsem.h
include/linux/sched.h
include/linux/sched/signal.h
include/linux/skb_array.h
include/linux/tcp.h
include/linux/umh.h
include/net/addrconf.h
include/net/bluetooth/hci_core.h
include/net/bonding.h
include/net/cfg80211.h
include/net/devlink.h
include/net/erspan.h
include/net/ip.h
include/net/ip6_fib.h
include/net/ip6_route.h
include/net/mac80211.h
include/net/netfilter/nf_tables.h
include/net/netns/ipv4.h
include/net/pkt_cls.h
include/net/sch_generic.h
include/net/tcp.h
include/net/tls.h
include/trace/events/afs.h
include/trace/events/fib6.h
include/trace/events/xen.h
include/uapi/linux/bpf.h
include/uapi/linux/bpfilter.h [new file with mode: 0644]
include/uapi/linux/devlink.h
include/uapi/linux/elf.h
include/uapi/linux/netfilter/nf_conntrack_tcp.h
include/uapi/linux/nl80211.h
include/uapi/linux/pkt_cls.h
include/uapi/linux/rtnetlink.h
include/uapi/linux/snmp.h
include/uapi/linux/vmcore.h [new file with mode: 0644]
init/Kconfig
init/main.c
kernel/bpf/btf.c
kernel/bpf/core.c
kernel/bpf/sockmap.c
kernel/bpf/stackmap.c
kernel/bpf/syscall.c
kernel/bpf/verifier.c
kernel/events/ring_buffer.c
kernel/kthread.c
kernel/locking/rwsem-xadd.c
kernel/locking/rwsem.c
kernel/locking/rwsem.h
kernel/module.c
kernel/sched/autogroup.c
kernel/sched/core.c
kernel/sched/deadline.c
kernel/sched/fair.c
kernel/sched/rt.c
kernel/sched/sched.h
kernel/signal.c
kernel/stop_machine.c
kernel/time/tick-broadcast.c
kernel/umh.c
lib/find_bit_benchmark.c
lib/radix-tree.c
lib/swiotlb.c
lib/test_bitmap.c
lib/vsprintf.c
mm/Kconfig
mm/gup.c
mm/migrate.c
mm/mmap.c
mm/oom_kill.c
mm/sparse.c
mm/vmstat.c
mm/z3fold.c
net/8021q/vlan.c
net/8021q/vlan.h
net/8021q/vlan_netlink.c
net/Kconfig
net/Makefile
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_request.c
net/bpfilter/Kconfig [new file with mode: 0644]
net/bpfilter/Makefile [new file with mode: 0644]
net/bpfilter/bpfilter_kern.c [new file with mode: 0644]
net/bpfilter/main.c [new file with mode: 0644]
net/bpfilter/msgfmt.h [new file with mode: 0644]
net/bridge/netfilter/ebt_stp.c
net/core/dev.c
net/core/devlink.c
net/core/filter.c
net/core/sock.c
net/dsa/dsa2.c
net/ipv4/Makefile
net/ipv4/bpfilter/Makefile [new file with mode: 0644]
net/ipv4/bpfilter/sockopt.c [new file with mode: 0644]
net/ipv4/fib_frontend.c
net/ipv4/ip_gre.c
net/ipv4/ip_output.c
net/ipv4/ip_sockglue.c
net/ipv4/netfilter/ip_tables.c
net/ipv4/netfilter/ipt_rpfilter.c
net/ipv4/netlink.c [new file with mode: 0644]
net/ipv4/proc.c
net/ipv4/route.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_output.c
net/ipv4/tcp_recovery.c
net/ipv4/tcp_timer.c
net/ipv4/udp.c
net/ipv6/addrconf_core.c
net/ipv6/af_inet6.c
net/ipv6/fib6_rules.c
net/ipv6/ip6_fib.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_output.c
net/ipv6/netfilter/ip6_tables.c
net/ipv6/route.c
net/ipv6/udp.c
net/mac80211/cfg.c
net/mac80211/driver-ops.h
net/mac80211/ethtool.c
net/mac80211/ht.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/rx.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/ncsi-rsp.c
net/netfilter/core.c
net/netfilter/ipvs/ip_vs_conn.c
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/nf_conntrack_proto_tcp.c
net/netfilter/nf_tables_api.c
net/netfilter/nf_tables_core.c
net/netfilter/nfnetlink_acct.c
net/netfilter/nfnetlink_cthelper.c
net/netfilter/nft_compat.c
net/netfilter/nft_immediate.c
net/netfilter/x_tables.c
net/packet/af_packet.c
net/rfkill/core.c
net/sched/act_api.c
net/sched/act_vlan.c
net/sched/sch_generic.c
net/sched/sch_red.c
net/sched/sch_tbf.c
net/sctp/outqueue.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_diag.c
net/smc/smc_ib.c
net/smc/smc_llc.c
net/smc/smc_llc.h
net/smc/smc_pnet.c
net/smc/smc_rx.c
net/smc/smc_tx.c
net/smc/smc_tx.h
net/tls/tls_sw.c
net/wireless/core.c
net/wireless/nl80211.c
net/wireless/rdev-ops.h
net/wireless/reg.c
net/wireless/sme.c
net/wireless/trace.h
net/wireless/util.c
net/xdp/xdp_umem.c
samples/bpf/Makefile
samples/bpf/bpf_insn.h [new file with mode: 0644]
samples/bpf/bpf_load.c
samples/bpf/bpf_load.h
samples/bpf/cookie_uid_helper_example.c
samples/bpf/cpustat_user.c
samples/bpf/fds_example.c
samples/bpf/lathist_user.c
samples/bpf/libbpf.h [deleted file]
samples/bpf/load_sock_ops.c
samples/bpf/lwt_len_hist_user.c
samples/bpf/map_perf_test_user.c
samples/bpf/sock_example.c
samples/bpf/sock_example.h
samples/bpf/sockex1_user.c
samples/bpf/sockex2_user.c
samples/bpf/sockex3_user.c
samples/bpf/syscall_tp_user.c
samples/bpf/tc_l2_redirect_user.c
samples/bpf/test_cgrp2_array_pin.c
samples/bpf/test_cgrp2_attach.c
samples/bpf/test_cgrp2_attach2.c
samples/bpf/test_cgrp2_sock.c
samples/bpf/test_cgrp2_sock2.c
samples/bpf/test_current_task_under_cgroup_user.c
samples/bpf/test_lru_dist.c
samples/bpf/test_map_in_map_user.c
samples/bpf/test_overhead_user.c
samples/bpf/test_probe_write_user_user.c
samples/bpf/trace_output_user.c
samples/bpf/tracex1_user.c
samples/bpf/tracex2_user.c
samples/bpf/tracex3_user.c
samples/bpf/tracex4_user.c
samples/bpf/tracex5_user.c
samples/bpf/tracex6_user.c
samples/bpf/tracex7_user.c
samples/bpf/xdp1_user.c
samples/bpf/xdp_adjust_tail_user.c
samples/bpf/xdp_fwd_kern.c [new file with mode: 0644]
samples/bpf/xdp_fwd_user.c [new file with mode: 0644]
samples/bpf/xdp_monitor_user.c
samples/bpf/xdp_redirect_cpu_user.c
samples/bpf/xdp_redirect_map_user.c
samples/bpf/xdp_redirect_user.c
samples/bpf/xdp_router_ipv4_user.c
samples/bpf/xdp_rxq_info_user.c
samples/bpf/xdp_tx_iptunnel_user.c
samples/bpf/xdpsock_user.c
scripts/faddr2line
security/selinux/hooks.c
sound/core/control_compat.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_realtek.c
sound/usb/mixer.c
sound/usb/stream.c
tools/arch/arm/include/uapi/asm/kvm.h
tools/arch/arm64/include/uapi/asm/kvm.h
tools/arch/x86/include/asm/cpufeatures.h
tools/bpf/bpftool/.gitignore [new file with mode: 0644]
tools/bpf/bpftool/map.c
tools/bpf/bpftool/map_perf_ring.c
tools/include/linux/spinlock.h
tools/include/uapi/asm/bitsperlong.h [new file with mode: 0644]
tools/include/uapi/asm/errno.h [new file with mode: 0644]
tools/include/uapi/linux/bpf.h
tools/include/uapi/linux/kvm.h
tools/lib/bpf/Makefile
tools/lib/bpf/bpf.c
tools/lib/bpf/bpf.h
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf.h
tools/objtool/arch/x86/include/asm/insn.h
tools/objtool/check.c
tools/objtool/elf.c
tools/objtool/elf.h
tools/perf/bench/numa.c
tools/perf/pmu-events/arch/x86/mapfile.csv
tools/perf/tests/shell/record+probe_libc_inet_pton.sh
tools/perf/util/annotate.c
tools/perf/util/cs-etm.c
tools/perf/util/parse-events.c
tools/perf/util/parse-events.y
tools/testing/radix-tree/Makefile
tools/testing/radix-tree/multiorder.c
tools/testing/radix-tree/test.c
tools/testing/radix-tree/test.h
tools/testing/selftests/bpf/.gitignore
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/bpf_helpers.h
tools/testing/selftests/bpf/bpf_rand.h [new file with mode: 0644]
tools/testing/selftests/bpf/test_btf.c
tools/testing/selftests/bpf/test_progs.c
tools/testing/selftests/bpf/test_sockhash_kern.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_sockmap.c
tools/testing/selftests/bpf/test_sockmap_kern.c
tools/testing/selftests/bpf/test_sockmap_kern.h [new file with mode: 0644]
tools/testing/selftests/bpf/test_verifier.c
tools/testing/selftests/bpf/trace_helpers.c
tools/testing/selftests/bpf/trace_helpers.h
tools/testing/selftests/bpf/urandom_read.c
tools/testing/selftests/kvm/Makefile
tools/testing/selftests/kvm/include/test_util.h
tools/testing/selftests/kvm/lib/kvm_util.c
tools/testing/selftests/kvm/sync_regs_test.c
tools/testing/selftests/kvm/vmx_tsc_adjust_test.c
tools/testing/selftests/net/Makefile
tools/testing/selftests/net/fib_rule_tests.sh [new file with mode: 0755]
tools/testing/selftests/net/fib_tests.sh
tools/testing/selftests/tc-testing/tc-tests/actions/ife.json
tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json
tools/testing/selftests/tc-testing/tc-tests/actions/police.json
tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json
tools/testing/selftests/uevent/Makefile [new file with mode: 0644]
tools/testing/selftests/uevent/config [new file with mode: 0644]
tools/testing/selftests/uevent/uevent_filtering.c [new file with mode: 0644]
tools/testing/selftests/x86/Makefile
tools/testing/selftests/x86/mov_ss_trap.c [new file with mode: 0644]
tools/testing/selftests/x86/mpx-mini-test.c
tools/testing/selftests/x86/pkey-helpers.h
tools/testing/selftests/x86/protection_keys.c
virt/kvm/arm/vgic/vgic-debug.c
virt/kvm/arm/vgic/vgic-its.c
virt/kvm/arm/vgic/vgic-v3.c
virt/kvm/arm/vgic/vgic.c

index 640f65e..8e69345 100644 (file)
@@ -244,3 +244,11 @@ Description:    read only
                 Returns 1 if the psl timebase register is synchronized
                 with the core timebase register, 0 otherwise.
 Users:          https://github.com/ibm-capi/libcxl
+
+What:           /sys/class/cxl/<card>/tunneled_ops_supported
+Date:           May 2018
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                Returns 1 if tunneled operations are supported in capi mode,
+                0 otherwise.
+Users:          https://github.com/ibm-capi/libcxl
diff --git a/Documentation/bpf/README.rst b/Documentation/bpf/README.rst
new file mode 100644 (file)
index 0000000..b9a80c9
--- /dev/null
@@ -0,0 +1,36 @@
+=================
+BPF documentation
+=================
+
+This directory contains documentation for the BPF (Berkeley Packet
+Filter) facility, with a focus on the extended BPF version (eBPF).
+
+This kernel side documentation is still work in progress.  The main
+textual documentation is (for historical reasons) described in
+`Documentation/networking/filter.txt`_, which describe both classical
+and extended BPF instruction-set.
+The Cilium project also maintains a `BPF and XDP Reference Guide`_
+that goes into great technical depth about the BPF Architecture.
+
+The primary info for the bpf syscall is available in the `man-pages`_
+for `bpf(2)`_.
+
+
+
+Frequently asked questions (FAQ)
+================================
+
+Two sets of Questions and Answers (Q&A) are maintained.
+
+* QA for common questions about BPF see: bpf_design_QA_
+
+* QA for developers interacting with BPF subsystem: bpf_devel_QA_
+
+
+.. Links:
+.. _bpf_design_QA: bpf_design_QA.rst
+.. _bpf_devel_QA:  bpf_devel_QA.rst
+.. _Documentation/networking/filter.txt: ../networking/filter.txt
+.. _man-pages: https://www.kernel.org/doc/man-pages/
+.. _bpf(2): http://man7.org/linux/man-pages/man2/bpf.2.html
+.. _BPF and XDP Reference Guide: http://cilium.readthedocs.io/en/latest/bpf/
diff --git a/Documentation/bpf/bpf_design_QA.rst b/Documentation/bpf/bpf_design_QA.rst
new file mode 100644 (file)
index 0000000..6780a6d
--- /dev/null
@@ -0,0 +1,221 @@
+==============
+BPF Design Q&A
+==============
+
+BPF extensibility and applicability to networking, tracing, security
+in the linux kernel and several user space implementations of BPF
+virtual machine led to a number of misunderstanding on what BPF actually is.
+This short QA is an attempt to address that and outline a direction
+of where BPF is heading long term.
+
+.. contents::
+    :local:
+    :depth: 3
+
+Questions and Answers
+=====================
+
+Q: Is BPF a generic instruction set similar to x64 and arm64?
+-------------------------------------------------------------
+A: NO.
+
+Q: Is BPF a generic virtual machine ?
+-------------------------------------
+A: NO.
+
+BPF is generic instruction set *with* C calling convention.
+-----------------------------------------------------------
+
+Q: Why C calling convention was chosen?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A: Because BPF programs are designed to run in the linux kernel
+which is written in C, hence BPF defines instruction set compatible
+with two most used architectures x64 and arm64 (and takes into
+consideration important quirks of other architectures) and
+defines calling convention that is compatible with C calling
+convention of the linux kernel on those architectures.
+
+Q: can multiple return values be supported in the future?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A: NO. BPF allows only register R0 to be used as return value.
+
+Q: can more than 5 function arguments be supported in the future?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A: NO. BPF calling convention only allows registers R1-R5 to be used
+as arguments. BPF is not a standalone instruction set.
+(unlike x64 ISA that allows msft, cdecl and other conventions)
+
+Q: can BPF programs access instruction pointer or return address?
+-----------------------------------------------------------------
+A: NO.
+
+Q: can BPF programs access stack pointer ?
+------------------------------------------
+A: NO.
+
+Only frame pointer (register R10) is accessible.
+From compiler point of view it's necessary to have stack pointer.
+For example LLVM defines register R11 as stack pointer in its
+BPF backend, but it makes sure that generated code never uses it.
+
+Q: Does C-calling convention diminishes possible use cases?
+-----------------------------------------------------------
+A: YES.
+
+BPF design forces addition of major functionality in the form
+of kernel helper functions and kernel objects like BPF maps with
+seamless interoperability between them. It lets kernel call into
+BPF programs and programs call kernel helpers with zero overhead.
+As all of them were native C code. That is particularly the case
+for JITed BPF programs that are indistinguishable from
+native kernel C code.
+
+Q: Does it mean that 'innovative' extensions to BPF code are disallowed?
+------------------------------------------------------------------------
+A: Soft yes.
+
+At least for now until BPF core has support for
+bpf-to-bpf calls, indirect calls, loops, global variables,
+jump tables, read only sections and all other normal constructs
+that C code can produce.
+
+Q: Can loops be supported in a safe way?
+----------------------------------------
+A: It's not clear yet.
+
+BPF developers are trying to find a way to
+support bounded loops where the verifier can guarantee that
+the program terminates in less than 4096 instructions.
+
+Instruction level questions
+---------------------------
+
+Q: LD_ABS and LD_IND instructions vs C code
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Q: How come LD_ABS and LD_IND instruction are present in BPF whereas
+C code cannot express them and has to use builtin intrinsics?
+
+A: This is artifact of compatibility with classic BPF. Modern
+networking code in BPF performs better without them.
+See 'direct packet access'.
+
+Q: BPF instructions mapping not one-to-one to native CPU
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Q: It seems not all BPF instructions are one-to-one to native CPU.
+For example why BPF_JNE and other compare and jumps are not cpu-like?
+
+A: This was necessary to avoid introducing flags into ISA which are
+impossible to make generic and efficient across CPU architectures.
+
+Q: why BPF_DIV instruction doesn't map to x64 div?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A: Because if we picked one-to-one relationship to x64 it would have made
+it more complicated to support on arm64 and other archs. Also it
+needs div-by-zero runtime check.
+
+Q: why there is no BPF_SDIV for signed divide operation?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A: Because it would be rarely used. llvm errors in such case and
+prints a suggestion to use unsigned divide instead
+
+Q: Why BPF has implicit prologue and epilogue?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A: Because architectures like sparc have register windows and in general
+there are enough subtle differences between architectures, so naive
+store return address into stack won't work. Another reason is BPF has
+to be safe from division by zero (and legacy exception path
+of LD_ABS insn). Those instructions need to invoke epilogue and
+return implicitly.
+
+Q: Why BPF_JLT and BPF_JLE instructions were not introduced in the beginning?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A: Because classic BPF didn't have them and BPF authors felt that compiler
+workaround would be acceptable. Turned out that programs lose performance
+due to lack of these compare instructions and they were added.
+These two instructions is a perfect example what kind of new BPF
+instructions are acceptable and can be added in the future.
+These two already had equivalent instructions in native CPUs.
+New instructions that don't have one-to-one mapping to HW instructions
+will not be accepted.
+
+Q: BPF 32-bit subregister requirements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Q: BPF 32-bit subregisters have a requirement to zero upper 32-bits of BPF
+registers which makes BPF inefficient virtual machine for 32-bit
+CPU architectures and 32-bit HW accelerators. Can true 32-bit registers
+be added to BPF in the future?
+
+A: NO. The first thing to improve performance on 32-bit archs is to teach
+LLVM to generate code that uses 32-bit subregisters. Then second step
+is to teach verifier to mark operations where zero-ing upper bits
+is unnecessary. Then JITs can take advantage of those markings and
+drastically reduce size of generated code and improve performance.
+
+Q: Does BPF have a stable ABI?
+------------------------------
+A: YES. BPF instructions, arguments to BPF programs, set of helper
+functions and their arguments, recognized return codes are all part
+of ABI. However when tracing programs are using bpf_probe_read() helper
+to walk kernel internal datastructures and compile with kernel
+internal headers these accesses can and will break with newer
+kernels. The union bpf_attr -> kern_version is checked at load time
+to prevent accidentally loading kprobe-based bpf programs written
+for a different kernel. Networking programs don't do kern_version check.
+
+Q: How much stack space a BPF program uses?
+-------------------------------------------
+A: Currently all program types are limited to 512 bytes of stack
+space, but the verifier computes the actual amount of stack used
+and both interpreter and most JITed code consume necessary amount.
+
+Q: Can BPF be offloaded to HW?
+------------------------------
+A: YES. BPF HW offload is supported by NFP driver.
+
+Q: Does classic BPF interpreter still exist?
+--------------------------------------------
+A: NO. Classic BPF programs are converted into extend BPF instructions.
+
+Q: Can BPF call arbitrary kernel functions?
+-------------------------------------------
+A: NO. BPF programs can only call a set of helper functions which
+is defined for every program type.
+
+Q: Can BPF overwrite arbitrary kernel memory?
+---------------------------------------------
+A: NO.
+
+Tracing bpf programs can *read* arbitrary memory with bpf_probe_read()
+and bpf_probe_read_str() helpers. Networking programs cannot read
+arbitrary memory, since they don't have access to these helpers.
+Programs can never read or write arbitrary memory directly.
+
+Q: Can BPF overwrite arbitrary user memory?
+-------------------------------------------
+A: Sort-of.
+
+Tracing BPF programs can overwrite the user memory
+of the current task with bpf_probe_write_user(). Every time such
+program is loaded the kernel will print warning message, so
+this helper is only useful for experiments and prototypes.
+Tracing BPF programs are root only.
+
+Q: bpf_trace_printk() helper warning
+------------------------------------
+Q: When bpf_trace_printk() helper is used the kernel prints nasty
+warning message. Why is that?
+
+A: This is done to nudge program authors into better interfaces when
+programs need to pass data to user space. Like bpf_perf_event_output()
+can be used to efficiently stream data via perf ring buffer.
+BPF maps can be used for asynchronous data sharing between kernel
+and user space. bpf_trace_printk() should only be used for debugging.
+
+Q: New functionality via kernel modules?
+----------------------------------------
+Q: Can BPF functionality such as new program or map types, new
+helpers, etc be added out of kernel module code?
+
+A: NO.
diff --git a/Documentation/bpf/bpf_design_QA.txt b/Documentation/bpf/bpf_design_QA.txt
deleted file mode 100644 (file)
index f3e458a..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-BPF extensibility and applicability to networking, tracing, security
-in the linux kernel and several user space implementations of BPF
-virtual machine led to a number of misunderstanding on what BPF actually is.
-This short QA is an attempt to address that and outline a direction
-of where BPF is heading long term.
-
-Q: Is BPF a generic instruction set similar to x64 and arm64?
-A: NO.
-
-Q: Is BPF a generic virtual machine ?
-A: NO.
-
-BPF is generic instruction set _with_ C calling convention.
-
-Q: Why C calling convention was chosen?
-A: Because BPF programs are designed to run in the linux kernel
-   which is written in C, hence BPF defines instruction set compatible
-   with two most used architectures x64 and arm64 (and takes into
-   consideration important quirks of other architectures) and
-   defines calling convention that is compatible with C calling
-   convention of the linux kernel on those architectures.
-
-Q: can multiple return values be supported in the future?
-A: NO. BPF allows only register R0 to be used as return value.
-
-Q: can more than 5 function arguments be supported in the future?
-A: NO. BPF calling convention only allows registers R1-R5 to be used
-   as arguments. BPF is not a standalone instruction set.
-   (unlike x64 ISA that allows msft, cdecl and other conventions)
-
-Q: can BPF programs access instruction pointer or return address?
-A: NO.
-
-Q: can BPF programs access stack pointer ?
-A: NO. Only frame pointer (register R10) is accessible.
-   From compiler point of view it's necessary to have stack pointer.
-   For example LLVM defines register R11 as stack pointer in its
-   BPF backend, but it makes sure that generated code never uses it.
-
-Q: Does C-calling convention diminishes possible use cases?
-A: YES. BPF design forces addition of major functionality in the form
-   of kernel helper functions and kernel objects like BPF maps with
-   seamless interoperability between them. It lets kernel call into
-   BPF programs and programs call kernel helpers with zero overhead.
-   As all of them were native C code. That is particularly the case
-   for JITed BPF programs that are indistinguishable from
-   native kernel C code.
-
-Q: Does it mean that 'innovative' extensions to BPF code are disallowed?
-A: Soft yes. At least for now until BPF core has support for
-   bpf-to-bpf calls, indirect calls, loops, global variables,
-   jump tables, read only sections and all other normal constructs
-   that C code can produce.
-
-Q: Can loops be supported in a safe way?
-A: It's not clear yet. BPF developers are trying to find a way to
-   support bounded loops where the verifier can guarantee that
-   the program terminates in less than 4096 instructions.
-
-Q: How come LD_ABS and LD_IND instruction are present in BPF whereas
-   C code cannot express them and has to use builtin intrinsics?
-A: This is artifact of compatibility with classic BPF. Modern
-   networking code in BPF performs better without them.
-   See 'direct packet access'.
-
-Q: It seems not all BPF instructions are one-to-one to native CPU.
-   For example why BPF_JNE and other compare and jumps are not cpu-like?
-A: This was necessary to avoid introducing flags into ISA which are
-   impossible to make generic and efficient across CPU architectures.
-
-Q: why BPF_DIV instruction doesn't map to x64 div?
-A: Because if we picked one-to-one relationship to x64 it would have made
-   it more complicated to support on arm64 and other archs. Also it
-   needs div-by-zero runtime check.
-
-Q: why there is no BPF_SDIV for signed divide operation?
-A: Because it would be rarely used. llvm errors in such case and
-   prints a suggestion to use unsigned divide instead
-
-Q: Why BPF has implicit prologue and epilogue?
-A: Because architectures like sparc have register windows and in general
-   there are enough subtle differences between architectures, so naive
-   store return address into stack won't work. Another reason is BPF has
-   to be safe from division by zero (and legacy exception path
-   of LD_ABS insn). Those instructions need to invoke epilogue and
-   return implicitly.
-
-Q: Why BPF_JLT and BPF_JLE instructions were not introduced in the beginning?
-A: Because classic BPF didn't have them and BPF authors felt that compiler
-   workaround would be acceptable. Turned out that programs lose performance
-   due to lack of these compare instructions and they were added.
-   These two instructions is a perfect example what kind of new BPF
-   instructions are acceptable and can be added in the future.
-   These two already had equivalent instructions in native CPUs.
-   New instructions that don't have one-to-one mapping to HW instructions
-   will not be accepted.
-
-Q: BPF 32-bit subregisters have a requirement to zero upper 32-bits of BPF
-   registers which makes BPF inefficient virtual machine for 32-bit
-   CPU architectures and 32-bit HW accelerators. Can true 32-bit registers
-   be added to BPF in the future?
-A: NO. The first thing to improve performance on 32-bit archs is to teach
-   LLVM to generate code that uses 32-bit subregisters. Then second step
-   is to teach verifier to mark operations where zero-ing upper bits
-   is unnecessary. Then JITs can take advantage of those markings and
-   drastically reduce size of generated code and improve performance.
-
-Q: Does BPF have a stable ABI?
-A: YES. BPF instructions, arguments to BPF programs, set of helper
-   functions and their arguments, recognized return codes are all part
-   of ABI. However when tracing programs are using bpf_probe_read() helper
-   to walk kernel internal datastructures and compile with kernel
-   internal headers these accesses can and will break with newer
-   kernels. The union bpf_attr -> kern_version is checked at load time
-   to prevent accidentally loading kprobe-based bpf programs written
-   for a different kernel. Networking programs don't do kern_version check.
-
-Q: How much stack space a BPF program uses?
-A: Currently all program types are limited to 512 bytes of stack
-   space, but the verifier computes the actual amount of stack used
-   and both interpreter and most JITed code consume necessary amount.
-
-Q: Can BPF be offloaded to HW?
-A: YES. BPF HW offload is supported by NFP driver.
-
-Q: Does classic BPF interpreter still exist?
-A: NO. Classic BPF programs are converted into extend BPF instructions.
-
-Q: Can BPF call arbitrary kernel functions?
-A: NO. BPF programs can only call a set of helper functions which
-   is defined for every program type.
-
-Q: Can BPF overwrite arbitrary kernel memory?
-A: NO. Tracing bpf programs can _read_ arbitrary memory with bpf_probe_read()
-   and bpf_probe_read_str() helpers. Networking programs cannot read
-   arbitrary memory, since they don't have access to these helpers.
-   Programs can never read or write arbitrary memory directly.
-
-Q: Can BPF overwrite arbitrary user memory?
-A: Sort-of. Tracing BPF programs can overwrite the user memory
-   of the current task with bpf_probe_write_user(). Every time such
-   program is loaded the kernel will print warning message, so
-   this helper is only useful for experiments and prototypes.
-   Tracing BPF programs are root only.
-
-Q: When bpf_trace_printk() helper is used the kernel prints nasty
-   warning message. Why is that?
-A: This is done to nudge program authors into better interfaces when
-   programs need to pass data to user space. Like bpf_perf_event_output()
-   can be used to efficiently stream data via perf ring buffer.
-   BPF maps can be used for asynchronous data sharing between kernel
-   and user space. bpf_trace_printk() should only be used for debugging.
-
-Q: Can BPF functionality such as new program or map types, new
-   helpers, etc be added out of kernel module code?
-A: NO.
diff --git a/Documentation/bpf/bpf_devel_QA.rst b/Documentation/bpf/bpf_devel_QA.rst
new file mode 100644 (file)
index 0000000..0e7c1d9
--- /dev/null
@@ -0,0 +1,640 @@
+=================================
+HOWTO interact with BPF subsystem
+=================================
+
+This document provides information for the BPF subsystem about various
+workflows related to reporting bugs, submitting patches, and queueing
+patches for stable kernels.
+
+For general information about submitting patches, please refer to
+`Documentation/process/`_. This document only describes additional specifics
+related to BPF.
+
+.. contents::
+    :local:
+    :depth: 2
+
+Reporting bugs
+==============
+
+Q: How do I report bugs for BPF kernel code?
+--------------------------------------------
+A: Since all BPF kernel development as well as bpftool and iproute2 BPF
+loader development happens through the netdev kernel mailing list,
+please report any found issues around BPF to the following mailing
+list:
+
+ netdev@vger.kernel.org
+
+This may also include issues related to XDP, BPF tracing, etc.
+
+Given netdev has a high volume of traffic, please also add the BPF
+maintainers to Cc (from kernel MAINTAINERS_ file):
+
+* Alexei Starovoitov <ast@kernel.org>
+* Daniel Borkmann <daniel@iogearbox.net>
+
+In case a buggy commit has already been identified, make sure to keep
+the actual commit authors in Cc as well for the report. They can
+typically be identified through the kernel's git tree.
+
+**Please do NOT report BPF issues to bugzilla.kernel.org since it
+is a guarantee that the reported issue will be overlooked.**
+
+Submitting patches
+==================
+
+Q: To which mailing list do I need to submit my BPF patches?
+------------------------------------------------------------
+A: Please submit your BPF patches to the netdev kernel mailing list:
+
+ netdev@vger.kernel.org
+
+Historically, BPF came out of networking and has always been maintained
+by the kernel networking community. Although these days BPF touches
+many other subsystems as well, the patches are still routed mainly
+through the networking community.
+
+In case your patch has changes in various different subsystems (e.g.
+tracing, security, etc), make sure to Cc the related kernel mailing
+lists and maintainers from there as well, so they are able to review
+the changes and provide their Acked-by's to the patches.
+
+Q: Where can I find patches currently under discussion for BPF subsystem?
+-------------------------------------------------------------------------
+A: All patches that are Cc'ed to netdev are queued for review under netdev
+patchwork project:
+
+  http://patchwork.ozlabs.org/project/netdev/list/
+
+Those patches which target BPF, are assigned to a 'bpf' delegate for
+further processing from BPF maintainers. The current queue with
+patches under review can be found at:
+
+  https://patchwork.ozlabs.org/project/netdev/list/?delegate=77147
+
+Once the patches have been reviewed by the BPF community as a whole
+and approved by the BPF maintainers, their status in patchwork will be
+changed to 'Accepted' and the submitter will be notified by mail. This
+means that the patches look good from a BPF perspective and have been
+applied to one of the two BPF kernel trees.
+
+In case feedback from the community requires a respin of the patches,
+their status in patchwork will be set to 'Changes Requested', and purged
+from the current review queue. Likewise for cases where patches would
+get rejected or are not applicable to the BPF trees (but assigned to
+the 'bpf' delegate).
+
+Q: How do the changes make their way into Linux?
+------------------------------------------------
+A: There are two BPF kernel trees (git repositories). Once patches have
+been accepted by the BPF maintainers, they will be applied to one
+of the two BPF trees:
+
+ * https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git/
+ * https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/
+
+The bpf tree itself is for fixes only, whereas bpf-next for features,
+cleanups or other kind of improvements ("next-like" content). This is
+analogous to net and net-next trees for networking. Both bpf and
+bpf-next will only have a master branch in order to simplify against
+which branch patches should get rebased to.
+
+Accumulated BPF patches in the bpf tree will regularly get pulled
+into the net kernel tree. Likewise, accumulated BPF patches accepted
+into the bpf-next tree will make their way into net-next tree. net and
+net-next are both run by David S. Miller. From there, they will go
+into the kernel mainline tree run by Linus Torvalds. To read up on the
+process of net and net-next being merged into the mainline tree, see
+the `netdev FAQ`_ under:
+
+ `Documentation/networking/netdev-FAQ.txt`_
+
+Occasionally, to prevent merge conflicts, we might send pull requests
+to other trees (e.g. tracing) with a small subset of the patches, but
+net and net-next are always the main trees targeted for integration.
+
+The pull requests will contain a high-level summary of the accumulated
+patches and can be searched on netdev kernel mailing list through the
+following subject lines (``yyyy-mm-dd`` is the date of the pull
+request)::
+
+  pull-request: bpf yyyy-mm-dd
+  pull-request: bpf-next yyyy-mm-dd
+
+Q: How do I indicate which tree (bpf vs. bpf-next) my patch should be applied to?
+---------------------------------------------------------------------------------
+
+A: The process is the very same as described in the `netdev FAQ`_, so
+please read up on it. The subject line must indicate whether the
+patch is a fix or rather "next-like" content in order to let the
+maintainers know whether it is targeted at bpf or bpf-next.
+
+For fixes eventually landing in bpf -> net tree, the subject must
+look like::
+
+  git format-patch --subject-prefix='PATCH bpf' start..finish
+
+For features/improvements/etc that should eventually land in
+bpf-next -> net-next, the subject must look like::
+
+  git format-patch --subject-prefix='PATCH bpf-next' start..finish
+
+If unsure whether the patch or patch series should go into bpf
+or net directly, or bpf-next or net-next directly, it is not a
+problem either if the subject line says net or net-next as target.
+It is eventually up to the maintainers to do the delegation of
+the patches.
+
+If it is clear that patches should go into bpf or bpf-next tree,
+please make sure to rebase the patches against those trees in
+order to reduce potential conflicts.
+
+In case the patch or patch series has to be reworked and sent out
+again in a second or later revision, it is also required to add a
+version number (``v2``, ``v3``, ...) into the subject prefix::
+
+  git format-patch --subject-prefix='PATCH net-next v2' start..finish
+
+When changes have been requested to the patch series, always send the
+whole patch series again with the feedback incorporated (never send
+individual diffs on top of the old series).
+
+Q: What does it mean when a patch gets applied to bpf or bpf-next tree?
+-----------------------------------------------------------------------
+A: It means that the patch looks good for mainline inclusion from
+a BPF point of view.
+
+Be aware that this is not a final verdict that the patch will
+automatically get accepted into net or net-next trees eventually:
+
+On the netdev kernel mailing list reviews can come in at any point
+in time. If discussions around a patch conclude that they cannot
+get included as-is, we will either apply a follow-up fix or drop
+them from the trees entirely. Therefore, we also reserve to rebase
+the trees when deemed necessary. After all, the purpose of the tree
+is to:
+
+i) accumulate and stage BPF patches for integration into trees
+   like net and net-next, and
+
+ii) run extensive BPF test suite and
+    workloads on the patches before they make their way any further.
+
+Once the BPF pull request was accepted by David S. Miller, then
+the patches end up in net or net-next tree, respectively, and
+make their way from there further into mainline. Again, see the
+`netdev FAQ`_ for additional information e.g. on how often they are
+merged to mainline.
+
+Q: How long do I need to wait for feedback on my BPF patches?
+-------------------------------------------------------------
+A: We try to keep the latency low. The usual time to feedback will
+be around 2 or 3 business days. It may vary depending on the
+complexity of changes and current patch load.
+
+Q: How often do you send pull requests to major kernel trees like net or net-next?
+----------------------------------------------------------------------------------
+
+A: Pull requests will be sent out rather often in order to not
+accumulate too many patches in bpf or bpf-next.
+
+As a rule of thumb, expect pull requests for each tree regularly
+at the end of the week. In some cases pull requests could additionally
+come also in the middle of the week depending on the current patch
+load or urgency.
+
+Q: Are patches applied to bpf-next when the merge window is open?
+-----------------------------------------------------------------
+A: For the time when the merge window is open, bpf-next will not be
+processed. This is roughly analogous to net-next patch processing,
+so feel free to read up on the `netdev FAQ`_ about further details.
+
+During those two weeks of merge window, we might ask you to resend
+your patch series once bpf-next is open again. Once Linus released
+a ``v*-rc1`` after the merge window, we continue processing of bpf-next.
+
+For non-subscribers to kernel mailing lists, there is also a status
+page run by David S. Miller on net-next that provides guidance:
+
+  http://vger.kernel.org/~davem/net-next.html
+
+Q: Verifier changes and test cases
+----------------------------------
+Q: I made a BPF verifier change, do I need to add test cases for
+BPF kernel selftests_?
+
+A: If the patch has changes to the behavior of the verifier, then yes,
+it is absolutely necessary to add test cases to the BPF kernel
+selftests_ suite. If they are not present and we think they are
+needed, then we might ask for them before accepting any changes.
+
+In particular, test_verifier.c is tracking a high number of BPF test
+cases, including a lot of corner cases that LLVM BPF back end may
+generate out of the restricted C code. Thus, adding test cases is
+absolutely crucial to make sure future changes do not accidentally
+affect prior use-cases. Thus, treat those test cases as: verifier
+behavior that is not tracked in test_verifier.c could potentially
+be subject to change.
+
+Q: samples/bpf preference vs selftests?
+---------------------------------------
+Q: When should I add code to `samples/bpf/`_ and when to BPF kernel
+selftests_ ?
+
+A: In general, we prefer additions to BPF kernel selftests_ rather than
+`samples/bpf/`_. The rationale is very simple: kernel selftests are
+regularly run by various bots to test for kernel regressions.
+
+The more test cases we add to BPF selftests, the better the coverage
+and the less likely it is that those could accidentally break. It is
+not that BPF kernel selftests cannot demo how a specific feature can
+be used.
+
+That said, `samples/bpf/`_ may be a good place for people to get started,
+so it might be advisable that simple demos of features could go into
+`samples/bpf/`_, but advanced functional and corner-case testing rather
+into kernel selftests.
+
+If your sample looks like a test case, then go for BPF kernel selftests
+instead!
+
+Q: When should I add code to the bpftool?
+-----------------------------------------
+A: The main purpose of bpftool (under tools/bpf/bpftool/) is to provide
+a central user space tool for debugging and introspection of BPF programs
+and maps that are active in the kernel. If UAPI changes related to BPF
+enable for dumping additional information of programs or maps, then
+bpftool should be extended as well to support dumping them.
+
+Q: When should I add code to iproute2's BPF loader?
+---------------------------------------------------
+A: For UAPI changes related to the XDP or tc layer (e.g. ``cls_bpf``),
+the convention is that those control-path related changes are added to
+iproute2's BPF loader as well from user space side. This is not only
+useful to have UAPI changes properly designed to be usable, but also
+to make those changes available to a wider user base of major
+downstream distributions.
+
+Q: Do you accept patches as well for iproute2's BPF loader?
+-----------------------------------------------------------
+A: Patches for the iproute2's BPF loader have to be sent to:
+
+  netdev@vger.kernel.org
+
+While those patches are not processed by the BPF kernel maintainers,
+please keep them in Cc as well, so they can be reviewed.
+
+The official git repository for iproute2 is run by Stephen Hemminger
+and can be found at:
+
+  https://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git/
+
+The patches need to have a subject prefix of '``[PATCH iproute2
+master]``' or '``[PATCH iproute2 net-next]``'. '``master``' or
+'``net-next``' describes the target branch where the patch should be
+applied to. Meaning, if kernel changes went into the net-next kernel
+tree, then the related iproute2 changes need to go into the iproute2
+net-next branch, otherwise they can be targeted at master branch. The
+iproute2 net-next branch will get merged into the master branch after
+the current iproute2 version from master has been released.
+
+Like BPF, the patches end up in patchwork under the netdev project and
+are delegated to 'shemminger' for further processing:
+
+  http://patchwork.ozlabs.org/project/netdev/list/?delegate=389
+
+Q: What is the minimum requirement before I submit my BPF patches?
+------------------------------------------------------------------
+A: When submitting patches, always take the time and properly test your
+patches *prior* to submission. Never rush them! If maintainers find
+that your patches have not been properly tested, it is a good way to
+get them grumpy. Testing patch submissions is a hard requirement!
+
+Note, fixes that go to bpf tree *must* have a ``Fixes:`` tag included.
+The same applies to fixes that target bpf-next, where the affected
+commit is in net-next (or in some cases bpf-next). The ``Fixes:`` tag is
+crucial in order to identify follow-up commits and tremendously helps
+for people having to do backporting, so it is a must have!
+
+We also don't accept patches with an empty commit message. Take your
+time and properly write up a high quality commit message, it is
+essential!
+
+Think about it this way: other developers looking at your code a month
+from now need to understand *why* a certain change has been done that
+way, and whether there have been flaws in the analysis or assumptions
+that the original author did. Thus providing a proper rationale and
+describing the use-case for the changes is a must.
+
+Patch submissions with >1 patch must have a cover letter which includes
+a high level description of the series. This high level summary will
+then be placed into the merge commit by the BPF maintainers such that
+it is also accessible from the git log for future reference.
+
+Q: Features changing BPF JIT and/or LLVM
+----------------------------------------
+Q: What do I need to consider when adding a new instruction or feature
+that would require BPF JIT and/or LLVM integration as well?
+
+A: We try hard to keep all BPF JITs up to date such that the same user
+experience can be guaranteed when running BPF programs on different
+architectures without having the program punt to the less efficient
+interpreter in case the in-kernel BPF JIT is enabled.
+
+If you are unable to implement or test the required JIT changes for
+certain architectures, please work together with the related BPF JIT
+developers in order to get the feature implemented in a timely manner.
+Please refer to the git log (``arch/*/net/``) to locate the necessary
+people for helping out.
+
+Also always make sure to add BPF test cases (e.g. test_bpf.c and
+test_verifier.c) for new instructions, so that they can receive
+broad test coverage and help run-time testing the various BPF JITs.
+
+In case of new BPF instructions, once the changes have been accepted
+into the Linux kernel, please implement support into LLVM's BPF back
+end. See LLVM_ section below for further information.
+
+Stable submission
+=================
+
+Q: I need a specific BPF commit in stable kernels. What should I do?
+--------------------------------------------------------------------
+A: In case you need a specific fix in stable kernels, first check whether
+the commit has already been applied in the related ``linux-*.y`` branches:
+
+  https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/
+
+If not the case, then drop an email to the BPF maintainers with the
+netdev kernel mailing list in Cc and ask for the fix to be queued up:
+
+  netdev@vger.kernel.org
+
+The process in general is the same as on netdev itself, see also the
+`netdev FAQ`_ document.
+
+Q: Do you also backport to kernels not currently maintained as stable?
+----------------------------------------------------------------------
+A: No. If you need a specific BPF commit in kernels that are currently not
+maintained by the stable maintainers, then you are on your own.
+
+The current stable and longterm stable kernels are all listed here:
+
+  https://www.kernel.org/
+
+Q: The BPF patch I am about to submit needs to go to stable as well
+-------------------------------------------------------------------
+What should I do?
+
+A: The same rules apply as with netdev patch submissions in general, see
+`netdev FAQ`_ under:
+
+  `Documentation/networking/netdev-FAQ.txt`_
+
+Never add "``Cc: stable@vger.kernel.org``" to the patch description, but
+ask the BPF maintainers to queue the patches instead. This can be done
+with a note, for example, under the ``---`` part of the patch which does
+not go into the git log. Alternatively, this can be done as a simple
+request by mail instead.
+
+Q: Queue stable patches
+-----------------------
+Q: Where do I find currently queued BPF patches that will be submitted
+to stable?
+
+A: Once patches that fix critical bugs got applied into the bpf tree, they
+are queued up for stable submission under:
+
+  http://patchwork.ozlabs.org/bundle/bpf/stable/?state=*
+
+They will be on hold there at minimum until the related commit made its
+way into the mainline kernel tree.
+
+After having been under broader exposure, the queued patches will be
+submitted by the BPF maintainers to the stable maintainers.
+
+Testing patches
+===============
+
+Q: How to run BPF selftests
+---------------------------
+A: After you have booted into the newly compiled kernel, navigate to
+the BPF selftests_ suite in order to test BPF functionality (current
+working directory points to the root of the cloned git tree)::
+
+  $ cd tools/testing/selftests/bpf/
+  $ make
+
+To run the verifier tests::
+
+  $ sudo ./test_verifier
+
+The verifier tests print out all the current checks being
+performed. The summary at the end of running all tests will dump
+information of test successes and failures::
+
+  Summary: 418 PASSED, 0 FAILED
+
+In order to run through all BPF selftests, the following command is
+needed::
+
+  $ sudo make run_tests
+
+See the kernels selftest `Documentation/dev-tools/kselftest.rst`_
+document for further documentation.
+
+Q: Which BPF kernel selftests version should I run my kernel against?
+---------------------------------------------------------------------
+A: If you run a kernel ``xyz``, then always run the BPF kernel selftests
+from that kernel ``xyz`` as well. Do not expect that the BPF selftest
+from the latest mainline tree will pass all the time.
+
+In particular, test_bpf.c and test_verifier.c have a large number of
+test cases and are constantly updated with new BPF test sequences, or
+existing ones are adapted to verifier changes e.g. due to verifier
+becoming smarter and being able to better track certain things.
+
+LLVM
+====
+
+Q: Where do I find LLVM with BPF support?
+-----------------------------------------
+A: The BPF back end for LLVM is upstream in LLVM since version 3.7.1.
+
+All major distributions these days ship LLVM with BPF back end enabled,
+so for the majority of use-cases it is not required to compile LLVM by
+hand anymore, just install the distribution provided package.
+
+LLVM's static compiler lists the supported targets through
+``llc --version``, make sure BPF targets are listed. Example::
+
+     $ llc --version
+     LLVM (http://llvm.org/):
+       LLVM version 6.0.0svn
+       Optimized build.
+       Default target: x86_64-unknown-linux-gnu
+       Host CPU: skylake
+
+       Registered Targets:
+         bpf    - BPF (host endian)
+         bpfeb  - BPF (big endian)
+         bpfel  - BPF (little endian)
+         x86    - 32-bit X86: Pentium-Pro and above
+         x86-64 - 64-bit X86: EM64T and AMD64
+
+For developers in order to utilize the latest features added to LLVM's
+BPF back end, it is advisable to run the latest LLVM releases. Support
+for new BPF kernel features such as additions to the BPF instruction
+set are often developed together.
+
+All LLVM releases can be found at: http://releases.llvm.org/
+
+Q: Got it, so how do I build LLVM manually anyway?
+--------------------------------------------------
+A: You need cmake and gcc-c++ as build requisites for LLVM. Once you have
+that set up, proceed with building the latest LLVM and clang version
+from the git repositories::
+
+     $ git clone http://llvm.org/git/llvm.git
+     $ cd llvm/tools
+     $ git clone --depth 1 http://llvm.org/git/clang.git
+     $ cd ..; mkdir build; cd build
+     $ cmake .. -DLLVM_TARGETS_TO_BUILD="BPF;X86" \
+                -DBUILD_SHARED_LIBS=OFF           \
+                -DCMAKE_BUILD_TYPE=Release        \
+                -DLLVM_BUILD_RUNTIME=OFF
+     $ make -j $(getconf _NPROCESSORS_ONLN)
+
+The built binaries can then be found in the build/bin/ directory, where
+you can point the PATH variable to.
+
+Q: Reporting LLVM BPF issues
+----------------------------
+Q: Should I notify BPF kernel maintainers about issues in LLVM's BPF code
+generation back end or about LLVM generated code that the verifier
+refuses to accept?
+
+A: Yes, please do!
+
+LLVM's BPF back end is a key piece of the whole BPF
+infrastructure and it ties deeply into verification of programs from the
+kernel side. Therefore, any issues on either side need to be investigated
+and fixed whenever necessary.
+
+Therefore, please make sure to bring them up at netdev kernel mailing
+list and Cc BPF maintainers for LLVM and kernel bits:
+
+* Yonghong Song <yhs@fb.com>
+* Alexei Starovoitov <ast@kernel.org>
+* Daniel Borkmann <daniel@iogearbox.net>
+
+LLVM also has an issue tracker where BPF related bugs can be found:
+
+  https://bugs.llvm.org/buglist.cgi?quicksearch=bpf
+
+However, it is better to reach out through mailing lists with having
+maintainers in Cc.
+
+Q: New BPF instruction for kernel and LLVM
+------------------------------------------
+Q: I have added a new BPF instruction to the kernel, how can I integrate
+it into LLVM?
+
+A: LLVM has a ``-mcpu`` selector for the BPF back end in order to allow
+the selection of BPF instruction set extensions. By default the
+``generic`` processor target is used, which is the base instruction set
+(v1) of BPF.
+
+LLVM has an option to select ``-mcpu=probe`` where it will probe the host
+kernel for supported BPF instruction set extensions and selects the
+optimal set automatically.
+
+For cross-compilation, a specific version can be select manually as well ::
+
+     $ llc -march bpf -mcpu=help
+     Available CPUs for this target:
+
+       generic - Select the generic processor.
+       probe   - Select the probe processor.
+       v1      - Select the v1 processor.
+       v2      - Select the v2 processor.
+     [...]
+
+Newly added BPF instructions to the Linux kernel need to follow the same
+scheme, bump the instruction set version and implement probing for the
+extensions such that ``-mcpu=probe`` users can benefit from the
+optimization transparently when upgrading their kernels.
+
+If you are unable to implement support for the newly added BPF instruction
+please reach out to BPF developers for help.
+
+By the way, the BPF kernel selftests run with ``-mcpu=probe`` for better
+test coverage.
+
+Q: clang flag for target bpf?
+-----------------------------
+Q: In some cases clang flag ``-target bpf`` is used but in other cases the
+default clang target, which matches the underlying architecture, is used.
+What is the difference and when I should use which?
+
+A: Although LLVM IR generation and optimization try to stay architecture
+independent, ``-target <arch>`` still has some impact on generated code:
+
+- BPF program may recursively include header file(s) with file scope
+  inline assembly codes. The default target can handle this well,
+  while ``bpf`` target may fail if bpf backend assembler does not
+  understand these assembly codes, which is true in most cases.
+
+- When compiled without ``-g``, additional elf sections, e.g.,
+  .eh_frame and .rela.eh_frame, may be present in the object file
+  with default target, but not with ``bpf`` target.
+
+- The default target may turn a C switch statement into a switch table
+  lookup and jump operation. Since the switch table is placed
+  in the global readonly section, the bpf program will fail to load.
+  The bpf target does not support switch table optimization.
+  The clang option ``-fno-jump-tables`` can be used to disable
+  switch table generation.
+
+- For clang ``-target bpf``, it is guaranteed that pointer or long /
+  unsigned long types will always have a width of 64 bit, no matter
+  whether underlying clang binary or default target (or kernel) is
+  32 bit. However, when native clang target is used, then it will
+  compile these types based on the underlying architecture's conventions,
+  meaning in case of 32 bit architecture, pointer or long / unsigned
+  long types e.g. in BPF context structure will have width of 32 bit
+  while the BPF LLVM back end still operates in 64 bit. The native
+  target is mostly needed in tracing for the case of walking ``pt_regs``
+  or other kernel structures where CPU's register width matters.
+  Otherwise, ``clang -target bpf`` is generally recommended.
+
+You should use default target when:
+
+- Your program includes a header file, e.g., ptrace.h, which eventually
+  pulls in some header files containing file scope host assembly codes.
+
+- You can add ``-fno-jump-tables`` to work around the switch table issue.
+
+Otherwise, you can use ``bpf`` target. Additionally, you *must* use bpf target
+when:
+
+- Your program uses data structures with pointer or long / unsigned long
+  types that interface with BPF helpers or context data structures. Access
+  into these structures is verified by the BPF verifier and may result
+  in verification failures if the native architecture is not aligned with
+  the BPF architecture, e.g. 64-bit. An example of this is
+  BPF_PROG_TYPE_SK_MSG require ``-target bpf``
+
+
+.. Links
+.. _Documentation/process/: https://www.kernel.org/doc/html/latest/process/
+.. _MAINTAINERS: ../../MAINTAINERS
+.. _Documentation/networking/netdev-FAQ.txt: ../networking/netdev-FAQ.txt
+.. _netdev FAQ: ../networking/netdev-FAQ.txt
+.. _samples/bpf/: ../../samples/bpf/
+.. _selftests: ../../tools/testing/selftests/bpf/
+.. _Documentation/dev-tools/kselftest.rst:
+   https://www.kernel.org/doc/html/latest/dev-tools/kselftest.html
+
+Happy BPF hacking!
diff --git a/Documentation/bpf/bpf_devel_QA.txt b/Documentation/bpf/bpf_devel_QA.txt
deleted file mode 100644 (file)
index da57601..0000000
+++ /dev/null
@@ -1,570 +0,0 @@
-This document provides information for the BPF subsystem about various
-workflows related to reporting bugs, submitting patches, and queueing
-patches for stable kernels.
-
-For general information about submitting patches, please refer to
-Documentation/process/. This document only describes additional specifics
-related to BPF.
-
-Reporting bugs:
----------------
-
-Q: How do I report bugs for BPF kernel code?
-
-A: Since all BPF kernel development as well as bpftool and iproute2 BPF
-   loader development happens through the netdev kernel mailing list,
-   please report any found issues around BPF to the following mailing
-   list:
-
-     netdev@vger.kernel.org
-
-   This may also include issues related to XDP, BPF tracing, etc.
-
-   Given netdev has a high volume of traffic, please also add the BPF
-   maintainers to Cc (from kernel MAINTAINERS file):
-
-     Alexei Starovoitov <ast@kernel.org>
-     Daniel Borkmann <daniel@iogearbox.net>
-
-   In case a buggy commit has already been identified, make sure to keep
-   the actual commit authors in Cc as well for the report. They can
-   typically be identified through the kernel's git tree.
-
-   Please do *not* report BPF issues to bugzilla.kernel.org since it
-   is a guarantee that the reported issue will be overlooked.
-
-Submitting patches:
--------------------
-
-Q: To which mailing list do I need to submit my BPF patches?
-
-A: Please submit your BPF patches to the netdev kernel mailing list:
-
-     netdev@vger.kernel.org
-
-   Historically, BPF came out of networking and has always been maintained
-   by the kernel networking community. Although these days BPF touches
-   many other subsystems as well, the patches are still routed mainly
-   through the networking community.
-
-   In case your patch has changes in various different subsystems (e.g.
-   tracing, security, etc), make sure to Cc the related kernel mailing
-   lists and maintainers from there as well, so they are able to review
-   the changes and provide their Acked-by's to the patches.
-
-Q: Where can I find patches currently under discussion for BPF subsystem?
-
-A: All patches that are Cc'ed to netdev are queued for review under netdev
-   patchwork project:
-
-     http://patchwork.ozlabs.org/project/netdev/list/
-
-   Those patches which target BPF, are assigned to a 'bpf' delegate for
-   further processing from BPF maintainers. The current queue with
-   patches under review can be found at:
-
-     https://patchwork.ozlabs.org/project/netdev/list/?delegate=77147
-
-   Once the patches have been reviewed by the BPF community as a whole
-   and approved by the BPF maintainers, their status in patchwork will be
-   changed to 'Accepted' and the submitter will be notified by mail. This
-   means that the patches look good from a BPF perspective and have been
-   applied to one of the two BPF kernel trees.
-
-   In case feedback from the community requires a respin of the patches,
-   their status in patchwork will be set to 'Changes Requested', and purged
-   from the current review queue. Likewise for cases where patches would
-   get rejected or are not applicable to the BPF trees (but assigned to
-   the 'bpf' delegate).
-
-Q: How do the changes make their way into Linux?
-
-A: There are two BPF kernel trees (git repositories). Once patches have
-   been accepted by the BPF maintainers, they will be applied to one
-   of the two BPF trees:
-
-     https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git/
-     https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/
-
-   The bpf tree itself is for fixes only, whereas bpf-next for features,
-   cleanups or other kind of improvements ("next-like" content). This is
-   analogous to net and net-next trees for networking. Both bpf and
-   bpf-next will only have a master branch in order to simplify against
-   which branch patches should get rebased to.
-
-   Accumulated BPF patches in the bpf tree will regularly get pulled
-   into the net kernel tree. Likewise, accumulated BPF patches accepted
-   into the bpf-next tree will make their way into net-next tree. net and
-   net-next are both run by David S. Miller. From there, they will go
-   into the kernel mainline tree run by Linus Torvalds. To read up on the
-   process of net and net-next being merged into the mainline tree, see
-   the netdev FAQ under:
-
-     Documentation/networking/netdev-FAQ.txt
-
-   Occasionally, to prevent merge conflicts, we might send pull requests
-   to other trees (e.g. tracing) with a small subset of the patches, but
-   net and net-next are always the main trees targeted for integration.
-
-   The pull requests will contain a high-level summary of the accumulated
-   patches and can be searched on netdev kernel mailing list through the
-   following subject lines (yyyy-mm-dd is the date of the pull request):
-
-     pull-request: bpf yyyy-mm-dd
-     pull-request: bpf-next yyyy-mm-dd
-
-Q: How do I indicate which tree (bpf vs. bpf-next) my patch should be
-   applied to?
-
-A: The process is the very same as described in the netdev FAQ, so
-   please read up on it. The subject line must indicate whether the
-   patch is a fix or rather "next-like" content in order to let the
-   maintainers know whether it is targeted at bpf or bpf-next.
-
-   For fixes eventually landing in bpf -> net tree, the subject must
-   look like:
-
-     git format-patch --subject-prefix='PATCH bpf' start..finish
-
-   For features/improvements/etc that should eventually land in
-   bpf-next -> net-next, the subject must look like:
-
-     git format-patch --subject-prefix='PATCH bpf-next' start..finish
-
-   If unsure whether the patch or patch series should go into bpf
-   or net directly, or bpf-next or net-next directly, it is not a
-   problem either if the subject line says net or net-next as target.
-   It is eventually up to the maintainers to do the delegation of
-   the patches.
-
-   If it is clear that patches should go into bpf or bpf-next tree,
-   please make sure to rebase the patches against those trees in
-   order to reduce potential conflicts.
-
-   In case the patch or patch series has to be reworked and sent out
-   again in a second or later revision, it is also required to add a
-   version number (v2, v3, ...) into the subject prefix:
-
-     git format-patch --subject-prefix='PATCH net-next v2' start..finish
-
-   When changes have been requested to the patch series, always send the
-   whole patch series again with the feedback incorporated (never send
-   individual diffs on top of the old series).
-
-Q: What does it mean when a patch gets applied to bpf or bpf-next tree?
-
-A: It means that the patch looks good for mainline inclusion from
-   a BPF point of view.
-
-   Be aware that this is not a final verdict that the patch will
-   automatically get accepted into net or net-next trees eventually:
-
-   On the netdev kernel mailing list reviews can come in at any point
-   in time. If discussions around a patch conclude that they cannot
-   get included as-is, we will either apply a follow-up fix or drop
-   them from the trees entirely. Therefore, we also reserve to rebase
-   the trees when deemed necessary. After all, the purpose of the tree
-   is to i) accumulate and stage BPF patches for integration into trees
-   like net and net-next, and ii) run extensive BPF test suite and
-   workloads on the patches before they make their way any further.
-
-   Once the BPF pull request was accepted by David S. Miller, then
-   the patches end up in net or net-next tree, respectively, and
-   make their way from there further into mainline. Again, see the
-   netdev FAQ for additional information e.g. on how often they are
-   merged to mainline.
-
-Q: How long do I need to wait for feedback on my BPF patches?
-
-A: We try to keep the latency low. The usual time to feedback will
-   be around 2 or 3 business days. It may vary depending on the
-   complexity of changes and current patch load.
-
-Q: How often do you send pull requests to major kernel trees like
-   net or net-next?
-
-A: Pull requests will be sent out rather often in order to not
-   accumulate too many patches in bpf or bpf-next.
-
-   As a rule of thumb, expect pull requests for each tree regularly
-   at the end of the week. In some cases pull requests could additionally
-   come also in the middle of the week depending on the current patch
-   load or urgency.
-
-Q: Are patches applied to bpf-next when the merge window is open?
-
-A: For the time when the merge window is open, bpf-next will not be
-   processed. This is roughly analogous to net-next patch processing,
-   so feel free to read up on the netdev FAQ about further details.
-
-   During those two weeks of merge window, we might ask you to resend
-   your patch series once bpf-next is open again. Once Linus released
-   a v*-rc1 after the merge window, we continue processing of bpf-next.
-
-   For non-subscribers to kernel mailing lists, there is also a status
-   page run by David S. Miller on net-next that provides guidance:
-
-     http://vger.kernel.org/~davem/net-next.html
-
-Q: I made a BPF verifier change, do I need to add test cases for
-   BPF kernel selftests?
-
-A: If the patch has changes to the behavior of the verifier, then yes,
-   it is absolutely necessary to add test cases to the BPF kernel
-   selftests suite. If they are not present and we think they are
-   needed, then we might ask for them before accepting any changes.
-
-   In particular, test_verifier.c is tracking a high number of BPF test
-   cases, including a lot of corner cases that LLVM BPF back end may
-   generate out of the restricted C code. Thus, adding test cases is
-   absolutely crucial to make sure future changes do not accidentally
-   affect prior use-cases. Thus, treat those test cases as: verifier
-   behavior that is not tracked in test_verifier.c could potentially
-   be subject to change.
-
-Q: When should I add code to samples/bpf/ and when to BPF kernel
-   selftests?
-
-A: In general, we prefer additions to BPF kernel selftests rather than
-   samples/bpf/. The rationale is very simple: kernel selftests are
-   regularly run by various bots to test for kernel regressions.
-
-   The more test cases we add to BPF selftests, the better the coverage
-   and the less likely it is that those could accidentally break. It is
-   not that BPF kernel selftests cannot demo how a specific feature can
-   be used.
-
-   That said, samples/bpf/ may be a good place for people to get started,
-   so it might be advisable that simple demos of features could go into
-   samples/bpf/, but advanced functional and corner-case testing rather
-   into kernel selftests.
-
-   If your sample looks like a test case, then go for BPF kernel selftests
-   instead!
-
-Q: When should I add code to the bpftool?
-
-A: The main purpose of bpftool (under tools/bpf/bpftool/) is to provide
-   a central user space tool for debugging and introspection of BPF programs
-   and maps that are active in the kernel. If UAPI changes related to BPF
-   enable for dumping additional information of programs or maps, then
-   bpftool should be extended as well to support dumping them.
-
-Q: When should I add code to iproute2's BPF loader?
-
-A: For UAPI changes related to the XDP or tc layer (e.g. cls_bpf), the
-   convention is that those control-path related changes are added to
-   iproute2's BPF loader as well from user space side. This is not only
-   useful to have UAPI changes properly designed to be usable, but also
-   to make those changes available to a wider user base of major
-   downstream distributions.
-
-Q: Do you accept patches as well for iproute2's BPF loader?
-
-A: Patches for the iproute2's BPF loader have to be sent to:
-
-     netdev@vger.kernel.org
-
-   While those patches are not processed by the BPF kernel maintainers,
-   please keep them in Cc as well, so they can be reviewed.
-
-   The official git repository for iproute2 is run by Stephen Hemminger
-   and can be found at:
-
-     https://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git/
-
-   The patches need to have a subject prefix of '[PATCH iproute2 master]'
-   or '[PATCH iproute2 net-next]'. 'master' or 'net-next' describes the
-   target branch where the patch should be applied to. Meaning, if kernel
-   changes went into the net-next kernel tree, then the related iproute2
-   changes need to go into the iproute2 net-next branch, otherwise they
-   can be targeted at master branch. The iproute2 net-next branch will get
-   merged into the master branch after the current iproute2 version from
-   master has been released.
-
-   Like BPF, the patches end up in patchwork under the netdev project and
-   are delegated to 'shemminger' for further processing:
-
-     http://patchwork.ozlabs.org/project/netdev/list/?delegate=389
-
-Q: What is the minimum requirement before I submit my BPF patches?
-
-A: When submitting patches, always take the time and properly test your
-   patches *prior* to submission. Never rush them! If maintainers find
-   that your patches have not been properly tested, it is a good way to
-   get them grumpy. Testing patch submissions is a hard requirement!
-
-   Note, fixes that go to bpf tree *must* have a Fixes: tag included. The
-   same applies to fixes that target bpf-next, where the affected commit
-   is in net-next (or in some cases bpf-next). The Fixes: tag is crucial
-   in order to identify follow-up commits and tremendously helps for people
-   having to do backporting, so it is a must have!
-
-   We also don't accept patches with an empty commit message. Take your
-   time and properly write up a high quality commit message, it is
-   essential!
-
-   Think about it this way: other developers looking at your code a month
-   from now need to understand *why* a certain change has been done that
-   way, and whether there have been flaws in the analysis or assumptions
-   that the original author did. Thus providing a proper rationale and
-   describing the use-case for the changes is a must.
-
-   Patch submissions with >1 patch must have a cover letter which includes
-   a high level description of the series. This high level summary will
-   then be placed into the merge commit by the BPF maintainers such that
-   it is also accessible from the git log for future reference.
-
-Q: What do I need to consider when adding a new instruction or feature
-   that would require BPF JIT and/or LLVM integration as well?
-
-A: We try hard to keep all BPF JITs up to date such that the same user
-   experience can be guaranteed when running BPF programs on different
-   architectures without having the program punt to the less efficient
-   interpreter in case the in-kernel BPF JIT is enabled.
-
-   If you are unable to implement or test the required JIT changes for
-   certain architectures, please work together with the related BPF JIT
-   developers in order to get the feature implemented in a timely manner.
-   Please refer to the git log (arch/*/net/) to locate the necessary
-   people for helping out.
-
-   Also always make sure to add BPF test cases (e.g. test_bpf.c and
-   test_verifier.c) for new instructions, so that they can receive
-   broad test coverage and help run-time testing the various BPF JITs.
-
-   In case of new BPF instructions, once the changes have been accepted
-   into the Linux kernel, please implement support into LLVM's BPF back
-   end. See LLVM section below for further information.
-
-Stable submission:
-------------------
-
-Q: I need a specific BPF commit in stable kernels. What should I do?
-
-A: In case you need a specific fix in stable kernels, first check whether
-   the commit has already been applied in the related linux-*.y branches:
-
-     https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/
-
-   If not the case, then drop an email to the BPF maintainers with the
-   netdev kernel mailing list in Cc and ask for the fix to be queued up:
-
-     netdev@vger.kernel.org
-
-   The process in general is the same as on netdev itself, see also the
-   netdev FAQ document.
-
-Q: Do you also backport to kernels not currently maintained as stable?
-
-A: No. If you need a specific BPF commit in kernels that are currently not
-   maintained by the stable maintainers, then you are on your own.
-
-   The current stable and longterm stable kernels are all listed here:
-
-     https://www.kernel.org/
-
-Q: The BPF patch I am about to submit needs to go to stable as well. What
-   should I do?
-
-A: The same rules apply as with netdev patch submissions in general, see
-   netdev FAQ under:
-
-     Documentation/networking/netdev-FAQ.txt
-
-   Never add "Cc: stable@vger.kernel.org" to the patch description, but
-   ask the BPF maintainers to queue the patches instead. This can be done
-   with a note, for example, under the "---" part of the patch which does
-   not go into the git log. Alternatively, this can be done as a simple
-   request by mail instead.
-
-Q: Where do I find currently queued BPF patches that will be submitted
-   to stable?
-
-A: Once patches that fix critical bugs got applied into the bpf tree, they
-   are queued up for stable submission under:
-
-     http://patchwork.ozlabs.org/bundle/bpf/stable/?state=*
-
-   They will be on hold there at minimum until the related commit made its
-   way into the mainline kernel tree.
-
-   After having been under broader exposure, the queued patches will be
-   submitted by the BPF maintainers to the stable maintainers.
-
-Testing patches:
-----------------
-
-Q: Which BPF kernel selftests version should I run my kernel against?
-
-A: If you run a kernel xyz, then always run the BPF kernel selftests from
-   that kernel xyz as well. Do not expect that the BPF selftest from the
-   latest mainline tree will pass all the time.
-
-   In particular, test_bpf.c and test_verifier.c have a large number of
-   test cases and are constantly updated with new BPF test sequences, or
-   existing ones are adapted to verifier changes e.g. due to verifier
-   becoming smarter and being able to better track certain things.
-
-LLVM:
------
-
-Q: Where do I find LLVM with BPF support?
-
-A: The BPF back end for LLVM is upstream in LLVM since version 3.7.1.
-
-   All major distributions these days ship LLVM with BPF back end enabled,
-   so for the majority of use-cases it is not required to compile LLVM by
-   hand anymore, just install the distribution provided package.
-
-   LLVM's static compiler lists the supported targets through 'llc --version',
-   make sure BPF targets are listed. Example:
-
-     $ llc --version
-     LLVM (http://llvm.org/):
-       LLVM version 6.0.0svn
-       Optimized build.
-       Default target: x86_64-unknown-linux-gnu
-       Host CPU: skylake
-
-       Registered Targets:
-         bpf    - BPF (host endian)
-         bpfeb  - BPF (big endian)
-         bpfel  - BPF (little endian)
-         x86    - 32-bit X86: Pentium-Pro and above
-         x86-64 - 64-bit X86: EM64T and AMD64
-
-   For developers in order to utilize the latest features added to LLVM's
-   BPF back end, it is advisable to run the latest LLVM releases. Support
-   for new BPF kernel features such as additions to the BPF instruction
-   set are often developed together.
-
-   All LLVM releases can be found at: http://releases.llvm.org/
-
-Q: Got it, so how do I build LLVM manually anyway?
-
-A: You need cmake and gcc-c++ as build requisites for LLVM. Once you have
-   that set up, proceed with building the latest LLVM and clang version
-   from the git repositories:
-
-     $ git clone http://llvm.org/git/llvm.git
-     $ cd llvm/tools
-     $ git clone --depth 1 http://llvm.org/git/clang.git
-     $ cd ..; mkdir build; cd build
-     $ cmake .. -DLLVM_TARGETS_TO_BUILD="BPF;X86" \
-                -DBUILD_SHARED_LIBS=OFF           \
-                -DCMAKE_BUILD_TYPE=Release        \
-                -DLLVM_BUILD_RUNTIME=OFF
-     $ make -j $(getconf _NPROCESSORS_ONLN)
-
-   The built binaries can then be found in the build/bin/ directory, where
-   you can point the PATH variable to.
-
-Q: Should I notify BPF kernel maintainers about issues in LLVM's BPF code
-   generation back end or about LLVM generated code that the verifier
-   refuses to accept?
-
-A: Yes, please do! LLVM's BPF back end is a key piece of the whole BPF
-   infrastructure and it ties deeply into verification of programs from the
-   kernel side. Therefore, any issues on either side need to be investigated
-   and fixed whenever necessary.
-
-   Therefore, please make sure to bring them up at netdev kernel mailing
-   list and Cc BPF maintainers for LLVM and kernel bits:
-
-     Yonghong Song <yhs@fb.com>
-     Alexei Starovoitov <ast@kernel.org>
-     Daniel Borkmann <daniel@iogearbox.net>
-
-   LLVM also has an issue tracker where BPF related bugs can be found:
-
-     https://bugs.llvm.org/buglist.cgi?quicksearch=bpf
-
-   However, it is better to reach out through mailing lists with having
-   maintainers in Cc.
-
-Q: I have added a new BPF instruction to the kernel, how can I integrate
-   it into LLVM?
-
-A: LLVM has a -mcpu selector for the BPF back end in order to allow the
-   selection of BPF instruction set extensions. By default the 'generic'
-   processor target is used, which is the base instruction set (v1) of BPF.
-
-   LLVM has an option to select -mcpu=probe where it will probe the host
-   kernel for supported BPF instruction set extensions and selects the
-   optimal set automatically.
-
-   For cross-compilation, a specific version can be select manually as well.
-
-     $ llc -march bpf -mcpu=help
-     Available CPUs for this target:
-
-       generic - Select the generic processor.
-       probe   - Select the probe processor.
-       v1      - Select the v1 processor.
-       v2      - Select the v2 processor.
-     [...]
-
-   Newly added BPF instructions to the Linux kernel need to follow the same
-   scheme, bump the instruction set version and implement probing for the
-   extensions such that -mcpu=probe users can benefit from the optimization
-   transparently when upgrading their kernels.
-
-   If you are unable to implement support for the newly added BPF instruction
-   please reach out to BPF developers for help.
-
-   By the way, the BPF kernel selftests run with -mcpu=probe for better
-   test coverage.
-
-Q: In some cases clang flag "-target bpf" is used but in other cases the
-   default clang target, which matches the underlying architecture, is used.
-   What is the difference and when I should use which?
-
-A: Although LLVM IR generation and optimization try to stay architecture
-   independent, "-target <arch>" still has some impact on generated code:
-
-     - BPF program may recursively include header file(s) with file scope
-       inline assembly codes. The default target can handle this well,
-       while bpf target may fail if bpf backend assembler does not
-       understand these assembly codes, which is true in most cases.
-
-     - When compiled without -g, additional elf sections, e.g.,
-       .eh_frame and .rela.eh_frame, may be present in the object file
-       with default target, but not with bpf target.
-
-     - The default target may turn a C switch statement into a switch table
-       lookup and jump operation. Since the switch table is placed
-       in the global readonly section, the bpf program will fail to load.
-       The bpf target does not support switch table optimization.
-       The clang option "-fno-jump-tables" can be used to disable
-       switch table generation.
-
-     - For clang -target bpf, it is guaranteed that pointer or long /
-       unsigned long types will always have a width of 64 bit, no matter
-       whether underlying clang binary or default target (or kernel) is
-       32 bit. However, when native clang target is used, then it will
-       compile these types based on the underlying architecture's conventions,
-       meaning in case of 32 bit architecture, pointer or long / unsigned
-       long types e.g. in BPF context structure will have width of 32 bit
-       while the BPF LLVM back end still operates in 64 bit. The native
-       target is mostly needed in tracing for the case of walking pt_regs
-       or other kernel structures where CPU's register width matters.
-       Otherwise, clang -target bpf is generally recommended.
-
-   You should use default target when:
-
-     - Your program includes a header file, e.g., ptrace.h, which eventually
-       pulls in some header files containing file scope host assembly codes.
-     - You can add "-fno-jump-tables" to work around the switch table issue.
-
-   Otherwise, you can use bpf target. Additionally, you _must_ use bpf target
-   when:
-
-     - Your program uses data structures with pointer or long / unsigned long
-       types that interface with BPF helpers or context data structures. Access
-       into these structures is verified by the BPF verifier and may result
-       in verification failures if the native architecture is not aligned with
-       the BPF architecture, e.g. 64-bit. An example of this is
-       BPF_PROG_TYPE_SK_MSG require '-target bpf'
-
-Happy BPF hacking!
index 9c67ee4..bbcb255 100644 (file)
@@ -2,7 +2,10 @@
 
 Required properties:
 
-- compatible: should be "qca,qca8337"
+- compatible: should be one of:
+    "qca,qca8334"
+    "qca,qca8337"
+
 - #size-cells: must be 0
 - #address-cells: must be 1
 
@@ -14,6 +17,20 @@ port and PHY id, each subnode describing a port needs to have a valid phandle
 referencing the internal PHY connected to it. The CPU port of this switch is
 always port 0.
 
+A CPU port node has the following optional node:
+
+- fixed-link            : Fixed-link subnode describing a link to a non-MDIO
+                          managed entity. See
+                          Documentation/devicetree/bindings/net/fixed-link.txt
+                          for details.
+
+For QCA8K the 'fixed-link' sub-node supports only the following properties:
+
+- 'speed' (integer, mandatory), to indicate the link speed. Accepted
+  values are 10, 100 and 1000
+- 'full-duplex' (boolean, optional), to indicate that full duplex is
+  used. When absent, half duplex is assumed.
+
 Example:
 
 
@@ -53,6 +70,10 @@ Example:
                                        label = "cpu";
                                        ethernet = <&gmac1>;
                                        phy-mode = "rgmii";
+                                       fixed-link {
+                                               speed = 1000;
+                                               full-duplex;
+                                       };
                                };
 
                                port@1 {
index 3d6d5fa..cfe7243 100644 (file)
@@ -7,6 +7,7 @@ Required properties:
 - compatible: must be one of the following string:
                "allwinner,sun8i-a83t-emac"
                "allwinner,sun8i-h3-emac"
+               "allwinner,sun8i-r40-gmac"
                "allwinner,sun8i-v3s-emac"
                "allwinner,sun50i-a64-emac"
 - reg: address and length of the register for the device.
@@ -20,18 +21,18 @@ Required properties:
 - phy-handle: See ethernet.txt
 - #address-cells: shall be 1
 - #size-cells: shall be 0
-- syscon: A phandle to the syscon of the SoC with one of the following
- compatible string:
-  - allwinner,sun8i-h3-system-controller
-  - allwinner,sun8i-v3s-system-controller
-  - allwinner,sun50i-a64-system-controller
-  - allwinner,sun8i-a83t-system-controller
+- syscon: A phandle to the device containing the EMAC or GMAC clock register
 
 Optional properties:
-- allwinner,tx-delay-ps: TX clock delay chain value in ps. Range value is 0-700. Default is 0)
-- allwinner,rx-delay-ps: RX clock delay chain value in ps. Range value is 0-3100. Default is 0)
-Both delay properties need to be a multiple of 100. They control the delay for
-external PHY.
+- allwinner,tx-delay-ps: TX clock delay chain value in ps.
+                        Range is 0-700. Default is 0.
+                        Unavailable for allwinner,sun8i-r40-gmac
+- allwinner,rx-delay-ps: RX clock delay chain value in ps.
+                        Range is 0-3100. Default is 0.
+                        Range is 0-700 for allwinner,sun8i-r40-gmac
+Both delay properties need to be a multiple of 100. They control the
+clock delay for external RGMII PHY. They do not apply to the internal
+PHY or external non-RGMII PHYs.
 
 Optional properties for the following compatibles:
   - "allwinner,sun8i-h3-emac",
index 1814fa1..fc019df 100644 (file)
@@ -21,9 +21,10 @@ Required properties:
        - main controller clock (for both armada-375-pp2 and armada-7k-pp2)
        - GOP clock (for both armada-375-pp2 and armada-7k-pp2)
        - MG clock (only for armada-7k-pp2)
+       - MG Core clock (only for armada-7k-pp2)
        - AXI clock (only for armada-7k-pp2)
-- clock-names: names of used clocks, must be "pp_clk", "gop_clk", "mg_clk"
-  and "axi_clk" (the 2 latter only for armada-7k-pp2).
+- clock-names: names of used clocks, must be "pp_clk", "gop_clk", "mg_clk",
+  "mg_core_clk" and "axi_clk" (the 3 latter only for armada-7k-pp2).
 
 The ethernet ports are represented by subnodes. At least one port is
 required.
@@ -80,8 +81,8 @@ cpm_ethernet: ethernet@0 {
        compatible = "marvell,armada-7k-pp22";
        reg = <0x0 0x100000>, <0x129000 0xb000>;
        clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>,
-                <&cpm_syscon0 1 5>, <&cpm_syscon0 1 18>;
-       clock-names = "pp_clk", "gop_clk", "gp_clk", "axi_clk";
+                <&cpm_syscon0 1 5>, <&cpm_syscon0 1 6>, <&cpm_syscon0 1 18>;
+       clock-names = "pp_clk", "gop_clk", "mg_clk", "mg_core_clk", "axi_clk";
 
        eth0: eth0 {
                interrupts = <ICU_GRP_NSR 39 IRQ_TYPE_LEVEL_HIGH>,
index 42a2483..e22d8cf 100644 (file)
@@ -57,6 +57,13 @@ KSZ9031:
       - txd2-skew-ps : Skew control of TX data 2 pad
       - txd3-skew-ps : Skew control of TX data 3 pad
 
+    - micrel,force-master:
+        Boolean, force phy to master mode. Only set this option if the phy
+        reference clock provided at CLK125_NDO pin is used as MAC reference
+        clock because the clock jitter in slave mode is to high (errata#2).
+        Attention: The link partner must be configurable as slave otherwise
+        no link will be established.
+
 Examples:
 
        mdio {
diff --git a/Documentation/devicetree/bindings/net/mscc-miim.txt b/Documentation/devicetree/bindings/net/mscc-miim.txt
new file mode 100644 (file)
index 0000000..7104679
--- /dev/null
@@ -0,0 +1,26 @@
+Microsemi MII Management Controller (MIIM) / MDIO
+=================================================
+
+Properties:
+- compatible: must be "mscc,ocelot-miim"
+- reg: The base address of the MDIO bus controller register bank. Optionally, a
+  second register bank can be defined if there is an associated reset register
+  for internal PHYs
+- #address-cells: Must be <1>.
+- #size-cells: Must be <0>.  MDIO addresses have no size component.
+- interrupts: interrupt specifier (refer to the interrupt binding)
+
+Typically an MDIO bus might have several children.
+
+Example:
+       mdio@107009c {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               compatible = "mscc,ocelot-miim";
+               reg = <0x107009c 0x36>, <0x10700f0 0x8>;
+               interrupts = <14>;
+
+               phy0: ethernet-phy@0 {
+                       reg = <0>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/net/mscc-ocelot.txt b/Documentation/devicetree/bindings/net/mscc-ocelot.txt
new file mode 100644 (file)
index 0000000..0a84711
--- /dev/null
@@ -0,0 +1,82 @@
+Microsemi Ocelot network Switch
+===============================
+
+The Microsemi Ocelot network switch can be found on Microsemi SoCs (VSC7513,
+VSC7514)
+
+Required properties:
+- compatible: Should be "mscc,vsc7514-switch"
+- reg: Must contain an (offset, length) pair of the register set for each
+  entry in reg-names.
+- reg-names: Must include the following entries:
+  - "sys"
+  - "rew"
+  - "qs"
+  - "hsio"
+  - "qsys"
+  - "ana"
+  - "portX" with X from 0 to the number of last port index available on that
+    switch
+- interrupts: Should contain the switch interrupts for frame extraction and
+  frame injection
+- interrupt-names: should contain the interrupt names: "xtr", "inj"
+- ethernet-ports: A container for child nodes representing switch ports.
+
+The ethernet-ports container has the following properties
+
+Required properties:
+
+- #address-cells: Must be 1
+- #size-cells: Must be 0
+
+Each port node must have the following mandatory properties:
+- reg: Describes the port address in the switch
+
+Port nodes may also contain the following optional standardised
+properties, described in binding documents:
+
+- phy-handle: Phandle to a PHY on an MDIO bus. See
+  Documentation/devicetree/bindings/net/ethernet.txt for details.
+
+Example:
+
+       switch@1010000 {
+               compatible = "mscc,vsc7514-switch";
+               reg = <0x1010000 0x10000>,
+                     <0x1030000 0x10000>,
+                     <0x1080000 0x100>,
+                     <0x10d0000 0x10000>,
+                     <0x11e0000 0x100>,
+                     <0x11f0000 0x100>,
+                     <0x1200000 0x100>,
+                     <0x1210000 0x100>,
+                     <0x1220000 0x100>,
+                     <0x1230000 0x100>,
+                     <0x1240000 0x100>,
+                     <0x1250000 0x100>,
+                     <0x1260000 0x100>,
+                     <0x1270000 0x100>,
+                     <0x1280000 0x100>,
+                     <0x1800000 0x80000>,
+                     <0x1880000 0x10000>;
+               reg-names = "sys", "rew", "qs", "hsio", "port0",
+                           "port1", "port2", "port3", "port4", "port5",
+                           "port6", "port7", "port8", "port9", "port10",
+                           "qsys", "ana";
+               interrupts = <21 22>;
+               interrupt-names = "xtr", "inj";
+
+               ethernet-ports {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       port0: port@0 {
+                               reg = <0>;
+                               phy-handle = <&phy0>;
+                       };
+                       port1: port@1 {
+                               reg = <1>;
+                               phy-handle = <&phy1>;
+                       };
+               };
+       };
diff --git a/Documentation/devicetree/bindings/net/qualcomm-bluetooth.txt b/Documentation/devicetree/bindings/net/qualcomm-bluetooth.txt
new file mode 100644 (file)
index 0000000..0ea18a5
--- /dev/null
@@ -0,0 +1,30 @@
+Qualcomm Bluetooth Chips
+---------------------
+
+This documents the binding structure and common properties for serial
+attached Qualcomm devices.
+
+Serial attached Qualcomm devices shall be a child node of the host UART
+device the slave device is attached to.
+
+Required properties:
+ - compatible: should contain one of the following:
+   * "qcom,qca6174-bt"
+
+Optional properties:
+ - enable-gpios: gpio specifier used to enable chip
+ - clocks: clock provided to the controller (SUSCLK_32KHZ)
+
+Example:
+
+serial@7570000 {
+       label = "BT-UART";
+       status = "okay";
+
+       bluetooth {
+               compatible = "qcom,qca6174-bt";
+
+               enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>;
+               clocks = <&divclk4>;
+       };
+};
index 929591d..8321399 100644 (file)
@@ -7,11 +7,11 @@ Required properties:
   "sff,sfp" for SFP modules
   "sff,sff" for soldered down SFF modules
 
-Optional Properties:
-
 - i2c-bus : phandle of an I2C bus controller for the SFP two wire serial
   interface
 
+Optional Properties:
+
 - mod-def0-gpios : GPIO phandle and a specifier of the MOD-DEF0 (AKA Mod_ABS)
   module presence input gpio signal, active (module absent) high. Must
   not be present for SFF modules
index 5172799..82a4cf2 100644 (file)
@@ -14,6 +14,7 @@ Required properties:
              "renesas,ether-r8a7791"  if the device is a part of R8A7791 SoC.
              "renesas,ether-r8a7793"  if the device is a part of R8A7793 SoC.
              "renesas,ether-r8a7794"  if the device is a part of R8A7794 SoC.
+             "renesas,gether-r8a77980" if the device is a part of R8A77980 SoC.
              "renesas,ether-r7s72100" if the device is a part of R7S72100 SoC.
              "renesas,rcar-gen1-ether" for a generic R-Car Gen1 device.
              "renesas,rcar-gen2-ether" for a generic R-Car Gen2 or RZ/G1
index 3d2a031..7fd4e8c 100644 (file)
@@ -4,6 +4,7 @@ Required properties:
 - compatible: Should be one of the following:
        * "qcom,ath10k"
        * "qcom,ipq4019-wifi"
+       * "qcom,wcn3990-wifi"
 
 PCI based devices uses compatible string "qcom,ath10k" and takes calibration
 data along with board specific data via "qcom,ath10k-calibration-data".
@@ -18,8 +19,12 @@ In general, entry "qcom,ath10k-pre-calibration-data" and
 "qcom,ath10k-calibration-data" conflict with each other and only one
 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"
 - 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,
@@ -49,6 +54,8 @@ Optional properties:
                                 hw versions.
 - 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".
 
 Example (to supply the calibration data alone):
 
@@ -119,3 +126,27 @@ wifi0: wifi@a000000 {
        qcom,msi_base = <0x40>;
        qcom,ath10k-pre-calibration-data = [ 01 02 03 ... ];
 };
+
+Example (to supply wcn3990 SoC wifi block details):
+
+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"
+               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 */ >;
+               vdd-0.8-cx-mx-supply = <&pm8998_l5>;
+};
index 5032e12..e6b4ebb 100644 (file)
@@ -1142,6 +1142,7 @@ into a register from memory, the register's top 56 bits are known zero, while
 the low 8 are unknown - which is represented as the tnum (0x0; 0xff).  If we
 then OR this with 0x40, we get (0x40; 0xbf), then if we add 1 we get (0x0;
 0x1ff), because of potential carries.
+
 Besides arithmetic, the register state can also be updated by conditional
 branches.  For instance, if a SCALAR_VALUE is compared > 8, in the 'true' branch
 it will have a umin_value (unsigned minimum value) of 9, whereas in the 'false'
@@ -1150,14 +1151,16 @@ BPF_JSGE) would instead update the signed minimum/maximum values.  Information
 from the signed and unsigned bounds can be combined; for instance if a value is
 first tested < 8 and then tested s> 4, the verifier will conclude that the value
 is also > 4 and s< 8, since the bounds prevent crossing the sign boundary.
+
 PTR_TO_PACKETs with a variable offset part have an 'id', which is common to all
 pointers sharing that same variable offset.  This is important for packet range
-checks: after adding some variable to a packet pointer, if you then copy it to
-another register and (say) add a constant 4, both registers will share the same
-'id' but one will have a fixed offset of +4.  Then if it is bounds-checked and
-found to be less than a PTR_TO_PACKET_END, the other register is now known to
-have a safe range of at least 4 bytes.  See 'Direct packet access', below, for
-more on PTR_TO_PACKET ranges.
+checks: after adding a variable to a packet pointer register A, if you then copy
+it to another register B and then add a constant 4 to A, both registers will
+share the same 'id' but the A will have a fixed offset of +4.  Then if A is
+bounds-checked and found to be less than a PTR_TO_PACKET_END, the register B is
+now known to have a safe range of at least 4 bytes.  See 'Direct packet access',
+below, for more on PTR_TO_PACKET ranges.
+
 The 'id' field is also used on PTR_TO_MAP_VALUE_OR_NULL, common to all copies of
 the pointer returned from a map lookup.  This means that when one copy is
 checked and found to be non-NULL, all copies can become PTR_TO_MAP_VALUEs.
index 59afc9a..924bd51 100644 (file)
@@ -449,8 +449,10 @@ tcp_recovery - INTEGER
        features.
 
        RACK: 0x1 enables the RACK loss detection for fast detection of lost
-             retransmissions and tail drops.
+             retransmissions and tail drops. It also subsumes and disables
+             RFC6675 recovery for SACK connections.
        RACK: 0x2 makes RACK's reordering window static (min_rtt/4).
+       RACK: 0x4 disables RACK's DUPACK threshold heuristic
 
        Default: 0x1
 
@@ -523,6 +525,19 @@ tcp_rmem - vector of 3 INTEGERs: min, default, max
 tcp_sack - BOOLEAN
        Enable select acknowledgments (SACKS).
 
+tcp_comp_sack_delay_ns - LONG INTEGER
+       TCP tries to reduce number of SACK sent, using a timer
+       based on 5% of SRTT, capped by this sysctl, in nano seconds.
+       The default is 1ms, based on TSO autosizing period.
+
+       Default : 1,000,000 ns (1 ms)
+
+tcp_comp_sack_nr - INTEGER
+       Max numer of SACK that can be compressed.
+       Using 0 disables SACK compression.
+
+       Detault : 44
+
 tcp_slow_start_after_idle - BOOLEAN
        If set, provide RFC2861 behavior and time out the congestion
        window after an idle period.  An idle period is defined at
index d4f33eb..ab022dc 100644 (file)
@@ -72,8 +72,8 @@ KVM_FEATURE_CLOCKSOURCE_STABLE_BIT ||    24 || host will warn if no guest-side
 
 flag                               || value || meaning
 ==================================================================================
-KVM_HINTS_DEDICATED                ||     0 || guest checks this feature bit to
-                                   ||       || determine if there is vCPU pinning
-                                   ||       || and there is no vCPU over-commitment,
+KVM_HINTS_REALTIME                 ||     0 || guest checks this feature bit to
+                                   ||       || determine that vCPUs are never
+                                   ||       || preempted for an unlimited time,
                                    ||       || allowing optimizations
 ----------------------------------------------------------------------------------
index cecf461..032807a 100644 (file)
@@ -3690,7 +3690,6 @@ F:        drivers/cpufreq/arm_big_little_dt.c
 
 CPU POWER MONITORING SUBSYSTEM
 M:     Thomas Renninger <trenn@suse.com>
-M:     Shuah Khan <shuahkh@osg.samsung.com>
 M:     Shuah Khan <shuah@kernel.org>
 L:     linux-pm@vger.kernel.org
 S:     Maintained
@@ -4309,7 +4308,7 @@ F:        Documentation/driver-api/dma-buf.rst
 T:     git git://anongit.freedesktop.org/drm/drm-misc
 
 DMA GENERIC OFFLOAD ENGINE SUBSYSTEM
-M:     Vinod Koul <vinod.koul@intel.com>
+M:     Vinod Koul <vkoul@kernel.org>
 L:     dmaengine@vger.kernel.org
 Q:     https://patchwork.kernel.org/project/linux-dmaengine/list/
 S:     Maintained
@@ -7694,10 +7693,10 @@ F:      include/linux/sunrpc/
 F:     include/uapi/linux/sunrpc/
 
 KERNEL SELFTEST FRAMEWORK
-M:     Shuah Khan <shuahkh@osg.samsung.com>
 M:     Shuah Khan <shuah@kernel.org>
 L:     linux-kselftest@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git
+Q:     https://patchwork.kernel.org/project/linux-kselftest/list/
 S:     Maintained
 F:     tools/testing/selftests/
 F:     Documentation/dev-tools/kselftest*
@@ -8466,6 +8465,7 @@ M:        Vivien Didelot <vivien.didelot@savoirfairelinux.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/dsa/mv88e6xxx/
+F:     linux/platform_data/mv88e6xxx.h
 F:     Documentation/devicetree/bindings/net/dsa/marvell.txt
 
 MARVELL ARMADA DRM SUPPORT
@@ -9279,6 +9279,12 @@ F:       include/linux/cciss*.h
 F:     include/uapi/linux/cciss*.h
 F:     Documentation/scsi/smartpqi.txt
 
+MICROSEMI ETHERNET SWITCH DRIVER
+M:     Alexandre Belloni <alexandre.belloni@bootlin.com>
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     drivers/net/ethernet/mscc/
+
 MICROSOFT SURFACE PRO 3 BUTTON DRIVER
 M:     Chen Yu <yu.c.chen@intel.com>
 L:     platform-driver-x86@vger.kernel.org
@@ -12211,7 +12217,7 @@ F:      Documentation/s390/vfio-ccw.txt
 F:     include/uapi/linux/vfio_ccw.h
 
 S390 ZCRYPT DRIVER
-M:     Harald Freudenberger <freude@de.ibm.com>
+M:     Harald Freudenberger <freude@linux.ibm.com>
 L:     linux-s390@vger.kernel.org
 W:     http://www.ibm.com/developerworks/linux/linux390/
 S:     Supported
@@ -13255,6 +13261,12 @@ M:     Jan-Benedict Glaw <jbglaw@lug-owl.de>
 S:     Maintained
 F:     arch/alpha/kernel/srm_env.c
 
+ST STM32 I2C/SMBUS DRIVER
+M:     Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
+L:     linux-i2c@vger.kernel.org
+S:     Maintained
+F:     drivers/i2c/busses/i2c-stm32*
+
 STABLE BRANCH
 M:     Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 L:     stable@vger.kernel.org
@@ -13385,6 +13397,7 @@ F:      drivers/media/usb/stk1160/
 STMMAC ETHERNET DRIVER
 M:     Giuseppe Cavallaro <peppe.cavallaro@st.com>
 M:     Alexandre Torgue <alexandre.torgue@st.com>
+M:     Jose Abreu <joabreu@synopsys.com>
 L:     netdev@vger.kernel.org
 W:     http://www.stlinux.com
 S:     Supported
@@ -14641,7 +14654,6 @@ F:      drivers/usb/common/usb-otg-fsm.c
 
 USB OVER IP DRIVER
 M:     Valentina Manea <valentina.manea.m@gmail.com>
-M:     Shuah Khan <shuahkh@osg.samsung.com>
 M:     Shuah Khan <shuah@kernel.org>
 L:     linux-usb@vger.kernel.org
 S:     Maintained
index d0d2652..ec6f459 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 VERSION = 4
 PATCHLEVEL = 17
 SUBLEVEL = 0
-EXTRAVERSION = -rc4
+EXTRAVERSION = -rc6
 NAME = Merciless Moray
 
 # *DOCUMENTATION*
index 8e0d665..75dd23a 100644 (file)
@@ -464,6 +464,10 @@ config GCC_PLUGIN_LATENT_ENTROPY
 config GCC_PLUGIN_STRUCTLEAK
        bool "Force initialization of variables containing userspace addresses"
        depends on GCC_PLUGINS
+       # Currently STRUCTLEAK inserts initialization out of live scope of
+       # variables from KASAN point of view. This leads to KASAN false
+       # positive reports. Prohibit this combination for now.
+       depends on !KASAN_EXTRA
        help
          This plugin zero-initializes any structures containing a
          __user attribute. This can prevent some classes of information
index 45a6b9b..6a4e734 100644 (file)
@@ -117,11 +117,9 @@ ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
 asflags-y := -DZIMAGE
 
 # Supply kernel BSS size to the decompressor via a linker symbol.
-KBSS_SZ = $(shell $(CROSS_COMPILE)nm $(obj)/../../../../vmlinux | \
-               perl -e 'while (<>) { \
-                       $$bss_start=hex($$1) if /^([[:xdigit:]]+) B __bss_start$$/; \
-                       $$bss_end=hex($$1) if /^([[:xdigit:]]+) B __bss_stop$$/; \
-               }; printf "%d\n", $$bss_end - $$bss_start;')
+KBSS_SZ = $(shell echo $$(($$($(CROSS_COMPILE)nm $(obj)/../../../../vmlinux | \
+               sed -n -e 's/^\([^ ]*\) [AB] __bss_start$$/-0x\1/p' \
+                      -e 's/^\([^ ]*\) [AB] __bss_stop$$/+0x\1/p') )) )
 LDFLAGS_vmlinux = --defsym _kernel_bss_size=$(KBSS_SZ)
 # Supply ZRELADDR to the decompressor via a linker symbol.
 ifneq ($(CONFIG_AUTO_ZRELADDR),y)
index 45c8823..517e0e1 100644 (file)
 #if defined(CONFIG_DEBUG_ICEDCC)
 
 #if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V6K) || defined(CONFIG_CPU_V7)
-               .macro  loadsp, rb, tmp
+               .macro  loadsp, rb, tmp1, tmp2
                .endm
                .macro  writeb, ch, rb
                mcr     p14, 0, \ch, c0, c5, 0
                .endm
 #elif defined(CONFIG_CPU_XSCALE)
-               .macro  loadsp, rb, tmp
+               .macro  loadsp, rb, tmp1, tmp2
                .endm
                .macro  writeb, ch, rb
                mcr     p14, 0, \ch, c8, c0, 0
                .endm
 #else
-               .macro  loadsp, rb, tmp
+               .macro  loadsp, rb, tmp1, tmp2
                .endm
                .macro  writeb, ch, rb
                mcr     p14, 0, \ch, c1, c0, 0
@@ -57,7 +57,7 @@
                .endm
 
 #if defined(CONFIG_ARCH_SA1100)
-               .macro  loadsp, rb, tmp
+               .macro  loadsp, rb, tmp1, tmp2
                mov     \rb, #0x80000000        @ physical base address
 #ifdef CONFIG_DEBUG_LL_SER3
                add     \rb, \rb, #0x00050000   @ Ser3
@@ -66,8 +66,8 @@
 #endif
                .endm
 #else
-               .macro  loadsp, rb, tmp
-               addruart \rb, \tmp
+               .macro  loadsp, rb, tmp1, tmp2
+               addruart \rb, \tmp1, \tmp2
                .endm
 #endif
 #endif
@@ -561,8 +561,6 @@ not_relocated:      mov     r0, #0
                bl      decompress_kernel
                bl      cache_clean_flush
                bl      cache_off
-               mov     r1, r7                  @ restore architecture number
-               mov     r2, r8                  @ restore atags pointer
 
 #ifdef CONFIG_ARM_VIRT_EXT
                mrs     r0, spsr                @ Get saved CPU boot mode
@@ -1297,7 +1295,7 @@ phex:             adr     r3, phexbuf
                b       1b
 
 @ puts corrupts {r0, r1, r2, r3}
-puts:          loadsp  r3, r1
+puts:          loadsp  r3, r2, r1
 1:             ldrb    r2, [r0], #1
                teq     r2, #0
                moveq   pc, lr
@@ -1314,8 +1312,8 @@ puts:             loadsp  r3, r1
 @ putc corrupts {r0, r1, r2, r3}
 putc:
                mov     r2, r0
+               loadsp  r3, r1, r0
                mov     r0, #0
-               loadsp  r3, r1
                b       2b
 
 @ memdump corrupts {r0, r1, r2, r3, r10, r11, r12, lr}
@@ -1365,6 +1363,8 @@ __hyp_reentry_vectors:
 
 __enter_kernel:
                mov     r0, #0                  @ must be 0
+               mov     r1, r7                  @ restore architecture number
+               mov     r2, r8                  @ restore atags pointer
  ARM(          mov     pc, r4          )       @ call kernel
  M_CLASS(      add     r4, r4, #1      )       @ enter in Thumb mode for M class
  THUMB(                bx      r4              )       @ entry point is always ARM for A/R classes
index 699fdf9..9fe4f5a 100644 (file)
@@ -69,7 +69,7 @@
                timer@20200 {
                        compatible = "arm,cortex-a9-global-timer";
                        reg = <0x20200 0x100>;
-                       interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupts = <GIC_PPI 11 IRQ_TYPE_EDGE_RISING>;
                        clocks = <&periph_clk>;
                };
 
index a1f4d6d..0edf769 100644 (file)
@@ -21,8 +21,8 @@
                stdout-path = "serial2:115200n8";
        };
 
-       memory {
-               device_type = "memory";
+       memory@c0000000 {
+               /* 128 MB DDR2 SDRAM @ 0xc0000000 */
                reg = <0xc0000000 0x08000000>;
        };
 
index c66cf78..1201000 100644 (file)
@@ -7,10 +7,19 @@
  * Free Software Foundation;  either version 2 of the  License, or (at your
  * option) any later version.
  */
-#include "skeleton.dtsi"
 #include <dt-bindings/interrupt-controller/irq.h>
 
 / {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       chosen { };
+       aliases { };
+
+       memory@c0000000 {
+               device_type = "memory";
+               reg = <0xc0000000 0x0>;
+       };
+
        arm {
                #address-cells = <1>;
                #size-cells = <1>;
@@ -46,8 +55,6 @@
                pmx_core: pinmux@14120 {
                        compatible = "pinctrl-single";
                        reg = <0x14120 0x50>;
-                       #address-cells = <1>;
-                       #size-cells = <0>;
                        #pinctrl-cells = <2>;
                        pinctrl-single,bit-per-mux;
                        pinctrl-single,register-width = <32>;
index d6657b3..85d7b51 100644 (file)
@@ -10,7 +10,7 @@
 
 / {
        model = "DM8148 EVM";
-       compatible = "ti,dm8148-evm", "ti,dm8148";
+       compatible = "ti,dm8148-evm", "ti,dm8148", "ti,dm814";
 
        memory@80000000 {
                device_type = "memory";
index 63883b3..6418f9c 100644 (file)
@@ -9,7 +9,7 @@
 
 / {
        model = "HP t410 Smart Zero Client";
-       compatible = "hp,t410", "ti,dm8148";
+       compatible = "hp,t410", "ti,dm8148", "ti,dm814";
 
        memory@80000000 {
                device_type = "memory";
index c72a213..1d030d5 100644 (file)
@@ -10,7 +10,7 @@
 
 / {
        model = "DM8168 EVM";
-       compatible = "ti,dm8168-evm", "ti,dm8168";
+       compatible = "ti,dm8168-evm", "ti,dm8168", "ti,dm816";
 
        memory@80000000 {
                device_type = "memory";
index fee0547..31b824a 100644 (file)
@@ -10,7 +10,7 @@
 
 / {
        model = "DRA62x J5 Eco EVM";
-       compatible = "ti,dra62x-j5eco-evm", "ti,dra62x", "ti,dm8148";
+       compatible = "ti,dra62x-j5eco-evm", "ti,dra62x", "ti,dm8148", "ti,dm814";
 
        memory@80000000 {
                device_type = "memory";
index 0c99ac0..6464f25 100644 (file)
        };
 
        touchscreen@20 {
-               compatible = "syna,rmi4_i2c";
+               compatible = "syna,rmi4-i2c";
                reg = <0x20>;
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_ts>;
 
                rmi4-f11@11 {
                        reg = <0x11>;
-                       touch-inverted-y;
-                       touch-swapped-x-y;
+                       touchscreen-inverted-y;
+                       touchscreen-swapped-x-y;
                        syna,sensor-type = <1>;
                };
        };
index 4d42335..ce85b3c 100644 (file)
 
                        crypto: caam@30900000 {
                                compatible = "fsl,sec-v4.0";
+                               fsl,sec-era = <8>;
                                #address-cells = <1>;
                                #size-cells = <1>;
                                reg = <0x30900000 0x40000>;
index b47cac2..6fa7bba 100644 (file)
@@ -26,7 +26,7 @@
                gpio = <&gpio1 3 0>;   /* gpio_3 */
                startup-delay-us = <70000>;
                enable-active-high;
-               vin-supply = <&vmmc2>;
+               vin-supply = <&vaux3>;
        };
 
        /* HS USB Host PHY on PORT 1 */
@@ -82,6 +82,7 @@
                twl_audio: audio {
                        compatible = "ti,twl4030-audio";
                        codec {
+                               ti,hs_extmute_gpio = <&gpio2 25 GPIO_ACTIVE_HIGH>;
                        };
                };
        };
                pinctrl-single,pins = <
                        OMAP3_CORE1_IOPAD(0x21ba, PIN_INPUT | MUX_MODE0)        /* i2c1_scl.i2c1_scl */
                        OMAP3_CORE1_IOPAD(0x21bc, PIN_INPUT | MUX_MODE0)        /* i2c1_sda.i2c1_sda */
+                       OMAP3_CORE1_IOPAD(0x20ba, PIN_OUTPUT | MUX_MODE4)        /* gpmc_ncs6.gpio_57 */
                >;
        };
 };
        };
        wl127x_gpio: pinmux_wl127x_gpio_pin {
                pinctrl-single,pins = <
-                       OMAP3_WKUP_IOPAD(0x2a0c, PIN_INPUT | MUX_MODE4)         /* sys_boot0.gpio_2 */
+                       OMAP3_WKUP_IOPAD(0x2a0a, PIN_INPUT | MUX_MODE4)         /* sys_boot0.gpio_2 */
                        OMAP3_WKUP_IOPAD(0x2a0c, PIN_OUTPUT | MUX_MODE4)        /* sys_boot1.gpio_3 */
                >;
        };
 #include "twl4030.dtsi"
 #include "twl4030_omap3.dtsi"
 
+&vaux3 {
+       regulator-min-microvolt = <2800000>;
+       regulator-max-microvolt = <2800000>;
+};
+
 &twl {
        twl_power: power {
                compatible = "ti,twl4030-power-idle-osc-off", "ti,twl4030-power-idle";
index 063fdb6..f07f901 100644 (file)
                                port@0 {
                                        reg = <0>;
                                        adv7511_in: endpoint {
-                                               remote-endpoint = <&du_out_lvds0>;
+                                               remote-endpoint = <&lvds0_out>;
                                        };
                                };
 
        status = "okay";
 
        clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&cpg CPG_MOD 722>,
-                <&cpg CPG_MOD 726>, <&cpg CPG_MOD 725>,
                 <&x13_clk>, <&x2_clk>;
-       clock-names = "du.0", "du.1", "du.2", "lvds.0", "lvds.1",
-                     "dclkin.0", "dclkin.1";
+       clock-names = "du.0", "du.1", "du.2", "dclkin.0", "dclkin.1";
 
        ports {
                port@0 {
                                remote-endpoint = <&adv7123_in>;
                        };
                };
+       };
+};
+
+&lvds0 {
+       status = "okay";
+
+       ports {
                port@1 {
                        endpoint {
                                remote-endpoint = <&adv7511_in>;
                        };
                };
-               port@2 {
+       };
+};
+
+&lvds1 {
+       status = "okay";
+
+       ports {
+               port@1 {
                        lvds_connector: endpoint {
                        };
                };
index e4367ce..05a0fc2 100644 (file)
 
                du: display@feb00000 {
                        compatible = "renesas,du-r8a7790";
-                       reg = <0 0xfeb00000 0 0x70000>,
-                             <0 0xfeb90000 0 0x1c>,
-                             <0 0xfeb94000 0 0x1c>;
-                       reg-names = "du", "lvds.0", "lvds.1";
+                       reg = <0 0xfeb00000 0 0x70000>;
                        interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>,
                                     <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>,
                                     <GIC_SPI 269 IRQ_TYPE_LEVEL_HIGH>;
                        clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>,
-                                <&cpg CPG_MOD 722>, <&cpg CPG_MOD 726>,
-                                <&cpg CPG_MOD 725>;
-                       clock-names = "du.0", "du.1", "du.2", "lvds.0",
-                                     "lvds.1";
+                                <&cpg CPG_MOD 722>;
+                       clock-names = "du.0", "du.1", "du.2";
                        status = "disabled";
 
                        ports {
                                port@1 {
                                        reg = <1>;
                                        du_out_lvds0: endpoint {
+                                               remote-endpoint = <&lvds0_in>;
                                        };
                                };
                                port@2 {
                                        reg = <2>;
                                        du_out_lvds1: endpoint {
+                                               remote-endpoint = <&lvds1_in>;
+                                       };
+                               };
+                       };
+               };
+
+               lvds0: lvds@feb90000 {
+                       compatible = "renesas,r8a7790-lvds";
+                       reg = <0 0xfeb90000 0 0x1c>;
+                       clocks = <&cpg CPG_MOD 726>;
+                       power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
+                       resets = <&cpg 726>;
+                       status = "disabled";
+
+                       ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               port@0 {
+                                       reg = <0>;
+                                       lvds0_in: endpoint {
+                                               remote-endpoint = <&du_out_lvds0>;
+                                       };
+                               };
+                               port@1 {
+                                       reg = <1>;
+                                       lvds0_out: endpoint {
+                                       };
+                               };
+                       };
+               };
+
+               lvds1: lvds@feb94000 {
+                       compatible = "renesas,r8a7790-lvds";
+                       reg = <0 0xfeb94000 0 0x1c>;
+                       clocks = <&cpg CPG_MOD 725>;
+                       power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
+                       resets = <&cpg 725>;
+                       status = "disabled";
+
+                       ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               port@0 {
+                                       reg = <0>;
+                                       lvds1_in: endpoint {
+                                               remote-endpoint = <&du_out_lvds1>;
+                                       };
+                               };
+                               port@1 {
+                                       reg = <1>;
+                                       lvds1_out: endpoint {
                                        };
                                };
                        };
index f40321a..9d7213a 100644 (file)
        pinctrl-names = "default";
        status = "okay";
 
-       clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&cpg CPG_MOD 726>,
+       clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>,
                 <&x13_clk>, <&x2_clk>;
-       clock-names = "du.0", "du.1", "lvds.0",
-                     "dclkin.0", "dclkin.1";
+       clock-names = "du.0", "du.1", "dclkin.0", "dclkin.1";
 
        ports {
                port@0 {
                                remote-endpoint = <&adv7511_in>;
                        };
                };
+       };
+};
+
+&lvds0 {
+       status = "okay";
+
+       ports {
                port@1 {
                        lvds_connector: endpoint {
                        };
index c14e6fe..ae9ed9f 100644 (file)
        pinctrl-names = "default";
        status = "okay";
 
-       clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&cpg CPG_MOD 726>,
+       clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>,
                 <&x3_clk>, <&x16_clk>;
-       clock-names = "du.0", "du.1", "lvds.0",
-                     "dclkin.0", "dclkin.1";
+       clock-names = "du.0", "du.1", "dclkin.0", "dclkin.1";
 
        ports {
                port@0 {
        };
 };
 
+&lvds0 {
+       status = "okay";
+
+       ports {
+               port@1 {
+                       lvds_connector: endpoint {
+                       };
+               };
+       };
+};
+
 &rcar_sound {
        pinctrl-0 = <&ssi_pins &audio_clk_pins>;
        pinctrl-names = "default";
index f11dab7..506b208 100644 (file)
 
                du: display@feb00000 {
                        compatible = "renesas,du-r8a7791";
-                       reg = <0 0xfeb00000 0 0x40000>,
-                             <0 0xfeb90000 0 0x1c>;
-                       reg-names = "du", "lvds.0";
+                       reg = <0 0xfeb00000 0 0x40000>;
                        interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>,
                                     <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>;
                        clocks = <&cpg CPG_MOD 724>,
-                                <&cpg CPG_MOD 723>,
-                                <&cpg CPG_MOD 726>;
-                       clock-names = "du.0", "du.1", "lvds.0";
+                                <&cpg CPG_MOD 723>;
+                       clock-names = "du.0", "du.1";
                        status = "disabled";
 
                        ports {
                                port@1 {
                                        reg = <1>;
                                        du_out_lvds0: endpoint {
+                                               remote-endpoint = <&lvds0_in>;
+                                       };
+                               };
+                       };
+               };
+
+               lvds0: lvds@feb90000 {
+                       compatible = "renesas,r8a7791-lvds";
+                       reg = <0 0xfeb90000 0 0x1c>;
+                       clocks = <&cpg CPG_MOD 726>;
+                       power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
+                       resets = <&cpg 726>;
+                       status = "disabled";
+
+                       ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               port@0 {
+                                       reg = <0>;
+                                       lvds0_in: endpoint {
+                                               remote-endpoint = <&du_out_lvds0>;
+                                       };
+                               };
+                               port@1 {
+                                       reg = <1>;
+                                       lvds0_out: endpoint {
                                        };
                                };
                        };
index 9ed6961..96e117d 100644 (file)
        pinctrl-names = "default";
        status = "okay";
 
-       clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&cpg CPG_MOD 726>,
+       clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>,
                 <&x13_clk>, <&x2_clk>;
-       clock-names = "du.0", "du.1", "lvds.0",
-                     "dclkin.0", "dclkin.1";
+       clock-names = "du.0", "du.1", "dclkin.0", "dclkin.1";
 
        ports {
                port@0 {
                                remote-endpoint = <&adv7511_in>;
                        };
                };
+       };
+};
+
+&lvds0 {
+       ports {
                port@1 {
                        lvds_connector: endpoint {
                        };
index f9c5a55..4f52603 100644 (file)
 
                du: display@feb00000 {
                        compatible = "renesas,du-r8a7793";
-                       reg = <0 0xfeb00000 0 0x40000>,
-                             <0 0xfeb90000 0 0x1c>;
-                       reg-names = "du", "lvds.0";
+                       reg = <0 0xfeb00000 0 0x40000>;
                        interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>,
                                     <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>;
                        clocks = <&cpg CPG_MOD 724>,
-                                <&cpg CPG_MOD 723>,
-                                <&cpg CPG_MOD 726>;
-                       clock-names = "du.0", "du.1", "lvds.0";
+                                <&cpg CPG_MOD 723>;
+                       clock-names = "du.0", "du.1";
                        status = "disabled";
 
                        ports {
                                port@1 {
                                        reg = <1>;
                                        du_out_lvds0: endpoint {
+                                               remote-endpoint = <&lvds0_in>;
+                                       };
+                               };
+                       };
+               };
+
+               lvds0: lvds@feb90000 {
+                       compatible = "renesas,r8a7793-lvds";
+                       reg = <0 0xfeb90000 0 0x1c>;
+                       clocks = <&cpg CPG_MOD 726>;
+                       power-domains = <&sysc R8A7793_PD_ALWAYS_ON>;
+                       resets = <&cpg 726>;
+
+                       status = "disabled";
+
+                       ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               port@0 {
+                                       reg = <0>;
+                                       lvds0_in: endpoint {
+                                               remote-endpoint = <&du_out_lvds0>;
+                                       };
+                               };
+                               port@1 {
+                                       reg = <1>;
+                                       lvds0_out: endpoint {
                                        };
                                };
                        };
index 0a71364..983dd5c 100644 (file)
                phy_type = "ulpi";
                clocks = <&tegra_car TEGRA20_CLK_USB2>,
                         <&tegra_car TEGRA20_CLK_PLL_U>,
-                        <&tegra_car TEGRA20_CLK_PLL_P_OUT4>;
+                        <&tegra_car TEGRA20_CLK_CDEV2>;
                clock-names = "reg", "pll_u", "ulpi-link";
                resets = <&tegra_car 58>, <&tegra_car 22>;
                reset-names = "usb", "utmi-pads";
index bc8d4bb..9342904 100644 (file)
@@ -536,4 +536,14 @@ THUMB(     orr     \reg , \reg , #PSR_T_BIT        )
 #endif
        .endm
 
+#ifdef CONFIG_KPROBES
+#define _ASM_NOKPROBE(entry)                           \
+       .pushsection "_kprobe_blacklist", "aw" ;        \
+       .balign 4 ;                                     \
+       .long entry;                                    \
+       .popsection
+#else
+#define _ASM_NOKPROBE(entry)
+#endif
+
 #endif /* __ASM_ASSEMBLER_H__ */
index 707a1f0..f675162 100644 (file)
@@ -309,6 +309,22 @@ static inline unsigned int kvm_get_vmid_bits(void)
        return 8;
 }
 
+/*
+ * We are not in the kvm->srcu critical section most of the time, so we take
+ * the SRCU read lock here. Since we copy the data from the user page, we
+ * can immediately drop the lock again.
+ */
+static inline int kvm_read_guest_lock(struct kvm *kvm,
+                                     gpa_t gpa, void *data, unsigned long len)
+{
+       int srcu_idx = srcu_read_lock(&kvm->srcu);
+       int ret = kvm_read_guest(kvm, gpa, data, len);
+
+       srcu_read_unlock(&kvm->srcu, srcu_idx);
+
+       return ret;
+}
+
 static inline void *kvm_get_hyp_vector(void)
 {
        return kvm_ksym_ref(__kvm_hyp_vector);
diff --git a/arch/arm/include/uapi/asm/siginfo.h b/arch/arm/include/uapi/asm/siginfo.h
deleted file mode 100644 (file)
index d051388..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __ASM_SIGINFO_H
-#define __ASM_SIGINFO_H
-
-#include <asm-generic/siginfo.h>
-
-/*
- * SIGFPE si_codes
- */
-#ifdef __KERNEL__
-#define FPE_FIXME      0       /* Broken dup of SI_USER */
-#endif /* __KERNEL__ */
-
-#endif
index 6b38d7a..dd2eb5f 100644 (file)
@@ -83,7 +83,7 @@ void machine_crash_nonpanic_core(void *unused)
 {
        struct pt_regs regs;
 
-       crash_setup_regs(&regs, NULL);
+       crash_setup_regs(&regs, get_irq_regs());
        printk(KERN_DEBUG "CPU %u will stop doing anything useful since another CPU has crashed\n",
               smp_processor_id());
        crash_save_cpu(&regs, smp_processor_id());
@@ -95,6 +95,27 @@ void machine_crash_nonpanic_core(void *unused)
                cpu_relax();
 }
 
+void crash_smp_send_stop(void)
+{
+       static int cpus_stopped;
+       unsigned long msecs;
+
+       if (cpus_stopped)
+               return;
+
+       atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
+       smp_call_function(machine_crash_nonpanic_core, NULL, false);
+       msecs = 1000; /* Wait at most a second for the other cpus to stop */
+       while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
+               mdelay(1);
+               msecs--;
+       }
+       if (atomic_read(&waiting_for_crash_ipi) > 0)
+               pr_warn("Non-crashing CPUs did not react to IPI\n");
+
+       cpus_stopped = 1;
+}
+
 static void machine_kexec_mask_interrupts(void)
 {
        unsigned int i;
@@ -120,19 +141,8 @@ static void machine_kexec_mask_interrupts(void)
 
 void machine_crash_shutdown(struct pt_regs *regs)
 {
-       unsigned long msecs;
-
        local_irq_disable();
-
-       atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
-       smp_call_function(machine_crash_nonpanic_core, NULL, false);
-       msecs = 1000; /* Wait at most a second for the other cpus to stop */
-       while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
-               mdelay(1);
-               msecs--;
-       }
-       if (atomic_read(&waiting_for_crash_ipi) > 0)
-               pr_warn("Non-crashing CPUs did not react to IPI\n");
+       crash_smp_send_stop();
 
        crash_save_cpu(regs, smp_processor_id());
        machine_kexec_mask_interrupts();
index 5e3633c..2fe8710 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/uaccess.h>
 #include <linux/hardirq.h>
 #include <linux/kdebug.h>
+#include <linux/kprobes.h>
 #include <linux/module.h>
 #include <linux/kexec.h>
 #include <linux/bug.h>
@@ -417,7 +418,8 @@ void unregister_undef_hook(struct undef_hook *hook)
        raw_spin_unlock_irqrestore(&undef_lock, flags);
 }
 
-static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
+static nokprobe_inline
+int call_undef_hook(struct pt_regs *regs, unsigned int instr)
 {
        struct undef_hook *hook;
        unsigned long flags;
@@ -490,6 +492,7 @@ die_sig:
 
        arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6);
 }
+NOKPROBE_SYMBOL(do_undefinstr)
 
 /*
  * Handle FIQ similarly to NMI on x86 systems.
index df73914..746e780 100644 (file)
@@ -38,6 +38,7 @@ ENTRY(__get_user_1)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_1)
+_ASM_NOKPROBE(__get_user_1)
 
 ENTRY(__get_user_2)
        check_uaccess r0, 2, r1, r2, __get_user_bad
@@ -58,6 +59,7 @@ rb    .req    r0
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_2)
+_ASM_NOKPROBE(__get_user_2)
 
 ENTRY(__get_user_4)
        check_uaccess r0, 4, r1, r2, __get_user_bad
@@ -65,6 +67,7 @@ ENTRY(__get_user_4)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_4)
+_ASM_NOKPROBE(__get_user_4)
 
 ENTRY(__get_user_8)
        check_uaccess r0, 8, r1, r2, __get_user_bad8
@@ -78,6 +81,7 @@ ENTRY(__get_user_8)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_8)
+_ASM_NOKPROBE(__get_user_8)
 
 #ifdef __ARMEB__
 ENTRY(__get_user_32t_8)
@@ -91,6 +95,7 @@ ENTRY(__get_user_32t_8)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_32t_8)
+_ASM_NOKPROBE(__get_user_32t_8)
 
 ENTRY(__get_user_64t_1)
        check_uaccess r0, 1, r1, r2, __get_user_bad8
@@ -98,6 +103,7 @@ ENTRY(__get_user_64t_1)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_64t_1)
+_ASM_NOKPROBE(__get_user_64t_1)
 
 ENTRY(__get_user_64t_2)
        check_uaccess r0, 2, r1, r2, __get_user_bad8
@@ -114,6 +120,7 @@ rb  .req    r0
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_64t_2)
+_ASM_NOKPROBE(__get_user_64t_2)
 
 ENTRY(__get_user_64t_4)
        check_uaccess r0, 4, r1, r2, __get_user_bad8
@@ -121,6 +128,7 @@ ENTRY(__get_user_64t_4)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_64t_4)
+_ASM_NOKPROBE(__get_user_64t_4)
 #endif
 
 __get_user_bad8:
@@ -131,6 +139,8 @@ __get_user_bad:
        ret     lr
 ENDPROC(__get_user_bad)
 ENDPROC(__get_user_bad8)
+_ASM_NOKPROBE(__get_user_bad)
+_ASM_NOKPROBE(__get_user_bad8)
 
 .pushsection __ex_table, "a"
        .long   1b, __get_user_bad
index 004f9c8..d1e8ce7 100644 (file)
@@ -205,12 +205,17 @@ static const short da830_evm_mmc_sd_pins[] = {
        -1
 };
 
+#define DA830_MMCSD_WP_PIN             GPIO_TO_PIN(2, 1)
+#define DA830_MMCSD_CD_PIN             GPIO_TO_PIN(2, 2)
+
 static struct gpiod_lookup_table mmc_gpios_table = {
        .dev_id = "da830-mmc.0",
        .table = {
                /* gpio chip 1 contains gpio range 32-63 */
-               GPIO_LOOKUP("davinci_gpio.1", 2, "cd", GPIO_ACTIVE_LOW),
-               GPIO_LOOKUP("davinci_gpio.1", 1, "wp", GPIO_ACTIVE_LOW),
+               GPIO_LOOKUP("davinci_gpio.0", DA830_MMCSD_CD_PIN, "cd",
+                           GPIO_ACTIVE_LOW),
+               GPIO_LOOKUP("davinci_gpio.0", DA830_MMCSD_WP_PIN, "wp",
+                           GPIO_ACTIVE_LOW),
        },
 };
 
index 3063478..158ed9a 100644 (file)
@@ -763,12 +763,17 @@ static const short da850_evm_mcasp_pins[] __initconst = {
        -1
 };
 
+#define DA850_MMCSD_CD_PIN             GPIO_TO_PIN(4, 0)
+#define DA850_MMCSD_WP_PIN             GPIO_TO_PIN(4, 1)
+
 static struct gpiod_lookup_table mmc_gpios_table = {
        .dev_id = "da830-mmc.0",
        .table = {
                /* gpio chip 2 contains gpio range 64-95 */
-               GPIO_LOOKUP("davinci_gpio.2", 0, "cd", GPIO_ACTIVE_LOW),
-               GPIO_LOOKUP("davinci_gpio.2", 1, "wp", GPIO_ACTIVE_LOW),
+               GPIO_LOOKUP("davinci_gpio.0", DA850_MMCSD_CD_PIN, "cd",
+                           GPIO_ACTIVE_LOW),
+               GPIO_LOOKUP("davinci_gpio.0", DA850_MMCSD_WP_PIN, "wp",
+                           GPIO_ACTIVE_LOW),
        },
 };
 
index cb30637..23ab9e8 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/gpio.h>
 #include <linux/gpio/machine.h>
 #include <linux/clk.h>
+#include <linux/dm9000.h>
 #include <linux/videodev2.h>
 #include <media/i2c/tvp514x.h>
 #include <linux/spi/spi.h>
@@ -109,12 +110,15 @@ static struct platform_device davinci_nand_device = {
        },
 };
 
+#define DM355_I2C_SDA_PIN      GPIO_TO_PIN(0, 15)
+#define DM355_I2C_SCL_PIN      GPIO_TO_PIN(0, 14)
+
 static struct gpiod_lookup_table i2c_recovery_gpiod_table = {
-       .dev_id = "i2c_davinci",
+       .dev_id = "i2c_davinci.1",
        .table = {
-               GPIO_LOOKUP("davinci_gpio", 15, "sda",
+               GPIO_LOOKUP("davinci_gpio.0", DM355_I2C_SDA_PIN, "sda",
                            GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN),
-               GPIO_LOOKUP("davinci_gpio", 14, "scl",
+               GPIO_LOOKUP("davinci_gpio.0", DM355_I2C_SCL_PIN, "scl",
                            GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN),
        },
 };
@@ -179,11 +183,16 @@ static struct resource dm355evm_dm9000_rsrc[] = {
        },
 };
 
+static struct dm9000_plat_data dm335evm_dm9000_platdata;
+
 static struct platform_device dm355evm_dm9000 = {
        .name           = "dm9000",
        .id             = -1,
        .resource       = dm355evm_dm9000_rsrc,
        .num_resources  = ARRAY_SIZE(dm355evm_dm9000_rsrc),
+       .dev            = {
+               .platform_data = &dm335evm_dm9000_platdata,
+       },
 };
 
 static struct tvp514x_platform_data tvp5146_pdata = {
index 95b55aa..509e64a 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_data/pcf857x.h>
 #include <linux/platform_data/at24.h>
+#include <linux/platform_data/gpio-davinci.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/rawnand.h>
 #include <linux/mtd/partitions.h>
@@ -596,12 +597,15 @@ static struct i2c_board_info __initdata i2c_info[] =  {
        },
 };
 
+#define DM644X_I2C_SDA_PIN     GPIO_TO_PIN(2, 12)
+#define DM644X_I2C_SCL_PIN     GPIO_TO_PIN(2, 11)
+
 static struct gpiod_lookup_table i2c_recovery_gpiod_table = {
-       .dev_id = "i2c_davinci",
+       .dev_id = "i2c_davinci.1",
        .table = {
-               GPIO_LOOKUP("davinci_gpio", 44, "sda",
+               GPIO_LOOKUP("davinci_gpio.0", DM644X_I2C_SDA_PIN, "sda",
                            GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN),
-               GPIO_LOOKUP("davinci_gpio", 43, "scl",
+               GPIO_LOOKUP("davinci_gpio.0", DM644X_I2C_SCL_PIN, "scl",
                            GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN),
        },
 };
index 2d37f5b..a3c0d1e 100644 (file)
@@ -532,11 +532,12 @@ static struct vpif_display_config dm646x_vpif_display_config = {
        .set_clock      = set_vpif_clock,
        .subdevinfo     = dm646x_vpif_subdev,
        .subdev_count   = ARRAY_SIZE(dm646x_vpif_subdev),
+       .i2c_adapter_id = 1,
        .chan_config[0] = {
                .outputs = dm6467_ch0_outputs,
                .output_count = ARRAY_SIZE(dm6467_ch0_outputs),
        },
-       .card_name      = "DM646x EVM",
+       .card_name      = "DM646x EVM Video Display",
 };
 
 /**
@@ -674,6 +675,7 @@ static struct vpif_capture_config dm646x_vpif_capture_cfg = {
        .setup_input_channel_mode = setup_vpif_input_channel_mode,
        .subdev_info = vpif_capture_sdev_info,
        .subdev_count = ARRAY_SIZE(vpif_capture_sdev_info),
+       .i2c_adapter_id = 1,
        .chan_config[0] = {
                .inputs = dm6467_ch0_inputs,
                .input_count = ARRAY_SIZE(dm6467_ch0_inputs),
@@ -694,6 +696,7 @@ static struct vpif_capture_config dm646x_vpif_capture_cfg = {
                        .fid_pol = 0,
                },
        },
+       .card_name = "DM646x EVM Video Capture",
 };
 
 static void __init evm_init_video(void)
index 0d32042..be8b892 100644 (file)
@@ -123,12 +123,16 @@ static const short hawk_mmcsd0_pins[] = {
        -1
 };
 
+#define DA850_HAWK_MMCSD_CD_PIN                GPIO_TO_PIN(3, 12)
+#define DA850_HAWK_MMCSD_WP_PIN                GPIO_TO_PIN(3, 13)
+
 static struct gpiod_lookup_table mmc_gpios_table = {
        .dev_id = "da830-mmc.0",
        .table = {
-               /* CD: gpio3_12: gpio60: chip 1 contains gpio range 32-63*/
-               GPIO_LOOKUP("davinci_gpio.0", 28, "cd", GPIO_ACTIVE_LOW),
-               GPIO_LOOKUP("davinci_gpio.0", 29, "wp", GPIO_ACTIVE_LOW),
+               GPIO_LOOKUP("davinci_gpio.0", DA850_HAWK_MMCSD_CD_PIN, "cd",
+                           GPIO_ACTIVE_LOW),
+               GPIO_LOOKUP("davinci_gpio.0", DA850_HAWK_MMCSD_WP_PIN, "wp",
+                           GPIO_ACTIVE_LOW),
        },
 };
 
index 109ab1f..c32ca27 100644 (file)
@@ -488,7 +488,8 @@ static u8 dm646x_default_priorities[DAVINCI_N_AINTC_IRQ] = {
        [IRQ_DM646X_MCASP0TXINT]        = 7,
        [IRQ_DM646X_MCASP0RXINT]        = 7,
        [IRQ_DM646X_RESERVED_3]         = 7,
-       [IRQ_DM646X_MCASP1TXINT]        = 7,    /* clockevent */
+       [IRQ_DM646X_MCASP1TXINT]        = 7,
+       [IRQ_TINT0_TINT12]              = 7,    /* clockevent */
        [IRQ_TINT0_TINT34]              = 7,    /* clocksource */
        [IRQ_TINT1_TINT12]              = 7,    /* DSP timer */
        [IRQ_TINT1_TINT34]              = 7,    /* system tick */
index fe57e26..abca83d 100644 (file)
@@ -29,6 +29,7 @@ static struct dev_pm_domain keystone_pm_domain = {
 
 static struct pm_clk_notifier_block platform_domain_notifier = {
        .pm_domain = &keystone_pm_domain,
+       .con_ids = { NULL },
 };
 
 static const struct of_device_id of_keystone_table[] = {
index 793a24a..d7ca9e2 100644 (file)
@@ -58,22 +58,24 @@ static irqreturn_t deferred_fiq(int irq, void *dev_id)
                irq_num = gpio_to_irq(gpio);
                fiq_count = fiq_buffer[FIQ_CNT_INT_00 + gpio];
 
-               while (irq_counter[gpio] < fiq_count) {
-                       if (gpio != AMS_DELTA_GPIO_PIN_KEYBRD_CLK) {
-                               struct irq_data *d = irq_get_irq_data(irq_num);
-
-                               /*
-                                * It looks like handle_edge_irq() that
-                                * OMAP GPIO edge interrupts default to,
-                                * expects interrupt already unmasked.
-                                */
-                               if (irq_chip && irq_chip->irq_unmask)
+               if (irq_counter[gpio] < fiq_count &&
+                               gpio != AMS_DELTA_GPIO_PIN_KEYBRD_CLK) {
+                       struct irq_data *d = irq_get_irq_data(irq_num);
+
+                       /*
+                        * handle_simple_irq() that OMAP GPIO edge
+                        * interrupts default to since commit 80ac93c27441
+                        * requires interrupt already acked and unmasked.
+                        */
+                       if (irq_chip) {
+                               if (irq_chip->irq_ack)
+                                       irq_chip->irq_ack(d);
+                               if (irq_chip->irq_unmask)
                                        irq_chip->irq_unmask(d);
                        }
-                       generic_handle_irq(irq_num);
-
-                       irq_counter[gpio]++;
                }
+               for (; irq_counter[gpio] < fiq_count; irq_counter[gpio]++)
+                       generic_handle_irq(irq_num);
        }
        return IRQ_HANDLED;
 }
index 76eb6ec..1e6a967 100644 (file)
@@ -188,7 +188,7 @@ static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
                                       ((prev & OMAP_POWERSTATE_MASK) << 0));
                        trace_power_domain_target_rcuidle(pwrdm->name,
                                                          trace_state,
-                                                         smp_processor_id());
+                                                         raw_smp_processor_id());
                }
                break;
        default:
@@ -518,7 +518,7 @@ int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst)
        if (arch_pwrdm && arch_pwrdm->pwrdm_set_next_pwrst) {
                /* Trace the pwrdm desired target state */
                trace_power_domain_target_rcuidle(pwrdm->name, pwrst,
-                                                 smp_processor_id());
+                                                 raw_smp_processor_id());
                /* Program the pwrdm desired target state */
                ret = arch_pwrdm->pwrdm_set_next_pwrst(pwrdm, pwrst);
        }
index 82689b9..d3ea645 100644 (file)
@@ -234,18 +234,11 @@ static void jit_fill_hole(void *area, unsigned int size)
 #define SCRATCH_SIZE 80
 
 /* total stack size used in JITed code */
-#define _STACK_SIZE \
-       (ctx->prog->aux->stack_depth + \
-        + SCRATCH_SIZE + \
-        + 4 /* extra for skb_copy_bits buffer */)
-
-#define STACK_SIZE ALIGN(_STACK_SIZE, STACK_ALIGNMENT)
+#define _STACK_SIZE    (ctx->prog->aux->stack_depth + SCRATCH_SIZE)
+#define STACK_SIZE     ALIGN(_STACK_SIZE, STACK_ALIGNMENT)
 
 /* Get the offset of eBPF REGISTERs stored on scratch space. */
-#define STACK_VAR(off) (STACK_SIZE-off-4)
-
-/* Offset of skb_copy_bits buffer */
-#define SKB_BUFFER STACK_VAR(SCRATCH_SIZE)
+#define STACK_VAR(off) (STACK_SIZE - off)
 
 #if __LINUX_ARM_ARCH__ < 7
 
index bcdecc2..b2aa9b3 100644 (file)
@@ -165,13 +165,14 @@ optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
 {
        unsigned long flags;
        struct kprobe *p = &op->kp;
-       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+       struct kprobe_ctlblk *kcb;
 
        /* Save skipped registers */
        regs->ARM_pc = (unsigned long)op->kp.addr;
        regs->ARM_ORIG_r0 = ~0UL;
 
        local_irq_save(flags);
+       kcb = get_kprobe_ctlblk();
 
        if (kprobe_running()) {
                kprobes_inc_nmissed_count(&op->kp);
@@ -191,6 +192,7 @@ optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
 
        local_irq_restore(flags);
 }
+NOKPROBE_SYMBOL(optimized_callback)
 
 int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *orig)
 {
index 4c375e1..af4ee2c 100644 (file)
@@ -257,7 +257,7 @@ static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_
 
        if (exceptions == VFP_EXCEPTION_ERROR) {
                vfp_panic("unhandled bounce", inst);
-               vfp_raise_sigfpe(FPE_FIXME, regs);
+               vfp_raise_sigfpe(FPE_FLTINV, regs);
                return;
        }
 
index c0231d0..1ad8677 100644 (file)
                        reg = <0x14d60000 0x100>;
                        dmas = <&pdma0 31 &pdma0 30>;
                        dma-names = "tx", "rx";
-                       interrupts = <GIC_SPI 435 IRQ_TYPE_NONE>;
+                       interrupts = <GIC_SPI 435 IRQ_TYPE_LEVEL_HIGH>;
                        clocks = <&cmu_peric CLK_PCLK_I2S1>,
                                 <&cmu_peric CLK_PCLK_I2S1>,
                                 <&cmu_peric CLK_SCLK_I2S1>;
index 48cad79..ed2f123 100644 (file)
                        compatible = "marvell,armada-7k-pp22";
                        reg = <0x0 0x100000>, <0x129000 0xb000>;
                        clocks = <&CP110_LABEL(clk) 1 3>, <&CP110_LABEL(clk) 1 9>,
-                                <&CP110_LABEL(clk) 1 5>, <&CP110_LABEL(clk) 1 18>;
+                                <&CP110_LABEL(clk) 1 5>, <&CP110_LABEL(clk) 1 6>,
+                                <&CP110_LABEL(clk) 1 18>;
                        clock-names = "pp_clk", "gop_clk",
-                                     "mg_clk", "axi_clk";
+                                     "mg_clk", "mg_core_clk", "axi_clk";
                        marvell,system-controller = <&CP110_LABEL(syscon0)>;
                        status = "disabled";
                        dma-coherent;
                        #size-cells = <0>;
                        compatible = "marvell,xmdio";
                        reg = <0x12a600 0x10>;
+                       clocks = <&CP110_LABEL(clk) 1 5>,
+                                <&CP110_LABEL(clk) 1 6>, <&CP110_LABEL(clk) 1 18>;
                        status = "disabled";
                };
 
index a8baad7..13f57ff 100644 (file)
@@ -46,7 +46,7 @@
                                compatible = "ethernet-phy-ieee802.3-c22";
                                reg = <0x0>;
                                interrupt-parent = <&gpio>;
-                               interrupts = <TEGRA_MAIN_GPIO(M, 5) IRQ_TYPE_LEVEL_HIGH>;
+                               interrupts = <TEGRA_MAIN_GPIO(M, 5) IRQ_TYPE_LEVEL_LOW>;
                        };
                };
        };
index 24552f1..6a57387 100644 (file)
                        drive-strength = <2>;   /* 2 MA */
                };
        };
+
+       blsp1_uart1_default: blsp1_uart1_default {
+               mux {
+                       pins = "gpio41", "gpio42", "gpio43", "gpio44";
+                       function = "blsp_uart2";
+               };
+
+               config {
+                       pins = "gpio41", "gpio42", "gpio43", "gpio44";
+                       drive-strength = <16>;
+                       bias-disable;
+               };
+       };
+
+       blsp1_uart1_sleep: blsp1_uart1_sleep {
+               mux {
+                       pins = "gpio41", "gpio42", "gpio43", "gpio44";
+                       function = "gpio";
+               };
+
+               config {
+                       pins = "gpio41", "gpio42", "gpio43", "gpio44";
+                       drive-strength = <2>;
+                       bias-disable;
+               };
+       };
 };
index 59b29dd..6167af9 100644 (file)
                };
        };
 
+       bt_en_gpios: bt_en_gpios {
+               pinconf {
+                       pins = "gpio19";
+                       function = PMIC_GPIO_FUNC_NORMAL;
+                       output-low;
+                       power-source = <PM8994_GPIO_S4>; // 1.8V
+                       qcom,drive-strength = <PMIC_GPIO_STRENGTH_LOW>;
+                       bias-pull-down;
+               };
+       };
+
+       wlan_en_gpios: wlan_en_gpios {
+               pinconf {
+                       pins = "gpio8";
+                       function = PMIC_GPIO_FUNC_NORMAL;
+                       output-low;
+                       power-source = <PM8994_GPIO_S4>; // 1.8V
+                       qcom,drive-strength = <PMIC_GPIO_STRENGTH_LOW>;
+                       bias-pull-down;
+               };
+       };
+
        volume_up_gpio: pm8996_gpio2 {
                pinconf {
                        pins = "gpio2";
                };
        };
 
+       divclk4_pin_a: divclk4 {
+               pinconf {
+                       pins = "gpio18";
+                       function = PMIC_GPIO_FUNC_FUNC2;
+
+                       bias-disable;
+                       power-source = <PM8994_GPIO_S4>;
+               };
+       };
+
        usb3_vbus_det_gpio: pm8996_gpio22 {
                pinconf {
                        pins = "gpio22";
index 1c8f1b8..4b8bb02 100644 (file)
@@ -23,6 +23,7 @@
        aliases {
                serial0 = &blsp2_uart1;
                serial1 = &blsp2_uart2;
+               serial2 = &blsp1_uart1;
                i2c0    = &blsp1_i2c2;
                i2c1    = &blsp2_i2c1;
                i2c2    = &blsp2_i2c0;
                stdout-path = "serial0:115200n8";
        };
 
+       clocks {
+               divclk4: divclk4 {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <32768>;
+                       clock-output-names = "divclk4";
+
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&divclk4_pin_a>;
+               };
+       };
+
        soc {
+               serial@7570000 {
+                       label = "BT-UART";
+                       status = "okay";
+                       pinctrl-names = "default", "sleep";
+                       pinctrl-0 = <&blsp1_uart1_default>;
+                       pinctrl-1 = <&blsp1_uart1_sleep>;
+
+                       bluetooth {
+                               compatible = "qcom,qca6174-bt";
+
+                               /* bt_disable_n gpio */
+                               enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>;
+
+                               clocks = <&divclk4>;
+                       };
+               };
+
                serial@75b0000 {
                        label = "LS-UART1";
                        status = "okay";
                        pinctrl-0 = <&usb2_vbus_det_gpio>;
                };
 
+               bt_en: bt-en-1-8v {
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&bt_en_gpios>;
+                       compatible = "regulator-fixed";
+                       regulator-name = "bt-en-regulator";
+                       regulator-min-microvolt = <1800000>;
+                       regulator-max-microvolt = <1800000>;
+
+                       /* WLAN card specific delay */
+                       startup-delay-us = <70000>;
+                       enable-active-high;
+               };
+
+               wlan_en: wlan-en-1-8v {
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&wlan_en_gpios>;
+                       compatible = "regulator-fixed";
+                       regulator-name = "wlan-en-regulator";
+                       regulator-min-microvolt = <1800000>;
+                       regulator-max-microvolt = <1800000>;
+
+                       gpio = <&pm8994_gpios 8 0>;
+
+                       /* WLAN card specific delay */
+                       startup-delay-us = <70000>;
+                       enable-active-high;
+               };
+
                agnoc@0 {
                        qcom,pcie@600000 {
+                               status = "okay";
                                perst-gpio = <&msmgpio 35 GPIO_ACTIVE_LOW>;
+                               vddpe-supply = <&wlan_en>;
+                               vddpe1-supply = <&bt_en>;
                        };
 
                        qcom,pcie@608000 {
index 410ae78..f8e49d0 100644 (file)
                        #clock-cells = <1>;
                };
 
+               blsp1_uart1: serial@7570000 {
+                       compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+                       reg = <0x07570000 0x1000>;
+                       interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>,
+                                <&gcc GCC_BLSP1_AHB_CLK>;
+                       clock-names = "core", "iface";
+                       status = "disabled";
+               };
+
                blsp1_spi0: spi@7575000 {
                        compatible = "qcom,spi-qup-v2.2.1";
                        reg = <0x07575000 0x600>;
index e62bda1..c32dd34 100644 (file)
                        mmc-ddr-1_8v;
                        mmc-hs200-1_8v;
                        mmc-pwrseq = <&emmc_pwrseq>;
-                       cdns,phy-input-delay-legacy = <4>;
+                       cdns,phy-input-delay-legacy = <9>;
                        cdns,phy-input-delay-mmc-highspeed = <2>;
                        cdns,phy-input-delay-mmc-ddr = <3>;
                        cdns,phy-dll-delay-sdclk = <21>;
index 2c1a92f..440c2e6 100644 (file)
                reg = <0>;
        };
 };
+
+&pinctrl_ether_rgmii {
+       tx {
+               pins = "RGMII_TXCLK", "RGMII_TXD0", "RGMII_TXD1",
+                      "RGMII_TXD2", "RGMII_TXD3", "RGMII_TXCTL";
+               drive-strength = <9>;
+       };
+};
index 9efe20d..3a5ed78 100644 (file)
                        mmc-ddr-1_8v;
                        mmc-hs200-1_8v;
                        mmc-pwrseq = <&emmc_pwrseq>;
-                       cdns,phy-input-delay-legacy = <4>;
+                       cdns,phy-input-delay-legacy = <9>;
                        cdns,phy-input-delay-mmc-highspeed = <2>;
                        cdns,phy-input-delay-mmc-ddr = <3>;
                        cdns,phy-dll-delay-sdclk = <21>;
index 7c8f710..e85d6dd 100644 (file)
                        mmc-ddr-1_8v;
                        mmc-hs200-1_8v;
                        mmc-pwrseq = <&emmc_pwrseq>;
-                       cdns,phy-input-delay-legacy = <4>;
+                       cdns,phy-input-delay-legacy = <9>;
                        cdns,phy-input-delay-mmc-highspeed = <2>;
                        cdns,phy-input-delay-mmc-ddr = <3>;
                        cdns,phy-dll-delay-sdclk = <21>;
index 0821109..6128992 100644 (file)
@@ -360,6 +360,22 @@ static inline unsigned int kvm_get_vmid_bits(void)
        return (cpuid_feature_extract_unsigned_field(reg, ID_AA64MMFR1_VMIDBITS_SHIFT) == 2) ? 16 : 8;
 }
 
+/*
+ * We are not in the kvm->srcu critical section most of the time, so we take
+ * the SRCU read lock here. Since we copy the data from the user page, we
+ * can immediately drop the lock again.
+ */
+static inline int kvm_read_guest_lock(struct kvm *kvm,
+                                     gpa_t gpa, void *data, unsigned long len)
+{
+       int srcu_idx = srcu_read_lock(&kvm->srcu);
+       int ret = kvm_read_guest(kvm, gpa, data, len);
+
+       srcu_read_unlock(&kvm->srcu, srcu_idx);
+
+       return ret;
+}
+
 #ifdef CONFIG_KVM_INDIRECT_VECTORS
 /*
  * EL2 vectors can be mapped and rerouted in a number of ways,
index 0b40c8f..a6fdaea 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/bpf.h>
 #include <linux/filter.h>
 #include <linux/printk.h>
-#include <linux/skbuff.h>
 #include <linux/slab.h>
 
 #include <asm/byteorder.h>
@@ -80,23 +79,66 @@ static inline void emit(const u32 insn, struct jit_ctx *ctx)
        ctx->idx++;
 }
 
+static inline void emit_a64_mov_i(const int is64, const int reg,
+                                 const s32 val, struct jit_ctx *ctx)
+{
+       u16 hi = val >> 16;
+       u16 lo = val & 0xffff;
+
+       if (hi & 0x8000) {
+               if (hi == 0xffff) {
+                       emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx);
+               } else {
+                       emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx);
+                       if (lo != 0xffff)
+                               emit(A64_MOVK(is64, reg, lo, 0), ctx);
+               }
+       } else {
+               emit(A64_MOVZ(is64, reg, lo, 0), ctx);
+               if (hi)
+                       emit(A64_MOVK(is64, reg, hi, 16), ctx);
+       }
+}
+
+static int i64_i16_blocks(const u64 val, bool inverse)
+{
+       return (((val >>  0) & 0xffff) != (inverse ? 0xffff : 0x0000)) +
+              (((val >> 16) & 0xffff) != (inverse ? 0xffff : 0x0000)) +
+              (((val >> 32) & 0xffff) != (inverse ? 0xffff : 0x0000)) +
+              (((val >> 48) & 0xffff) != (inverse ? 0xffff : 0x0000));
+}
+
 static inline void emit_a64_mov_i64(const int reg, const u64 val,
                                    struct jit_ctx *ctx)
 {
-       u64 tmp = val;
-       int shift = 0;
-
-       emit(A64_MOVZ(1, reg, tmp & 0xffff, shift), ctx);
-       tmp >>= 16;
-       shift += 16;
-       while (tmp) {
-               if (tmp & 0xffff)
-                       emit(A64_MOVK(1, reg, tmp & 0xffff, shift), ctx);
-               tmp >>= 16;
-               shift += 16;
+       u64 nrm_tmp = val, rev_tmp = ~val;
+       bool inverse;
+       int shift;
+
+       if (!(nrm_tmp >> 32))
+               return emit_a64_mov_i(0, reg, (u32)val, ctx);
+
+       inverse = i64_i16_blocks(nrm_tmp, true) < i64_i16_blocks(nrm_tmp, false);
+       shift = max(round_down((inverse ? (fls64(rev_tmp) - 1) :
+                                         (fls64(nrm_tmp) - 1)), 16), 0);
+       if (inverse)
+               emit(A64_MOVN(1, reg, (rev_tmp >> shift) & 0xffff, shift), ctx);
+       else
+               emit(A64_MOVZ(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx);
+       shift -= 16;
+       while (shift >= 0) {
+               if (((nrm_tmp >> shift) & 0xffff) != (inverse ? 0xffff : 0x0000))
+                       emit(A64_MOVK(1, reg, (nrm_tmp >> shift) & 0xffff, shift), ctx);
+               shift -= 16;
        }
 }
 
+/*
+ * 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.
+ */
 static inline void emit_addr_mov_i64(const int reg, const u64 val,
                                     struct jit_ctx *ctx)
 {
@@ -111,26 +153,6 @@ static inline void emit_addr_mov_i64(const int reg, const u64 val,
        }
 }
 
-static inline void emit_a64_mov_i(const int is64, const int reg,
-                                 const s32 val, struct jit_ctx *ctx)
-{
-       u16 hi = val >> 16;
-       u16 lo = val & 0xffff;
-
-       if (hi & 0x8000) {
-               if (hi == 0xffff) {
-                       emit(A64_MOVN(is64, reg, (u16)~lo, 0), ctx);
-               } else {
-                       emit(A64_MOVN(is64, reg, (u16)~hi, 16), ctx);
-                       emit(A64_MOVK(is64, reg, lo, 0), ctx);
-               }
-       } else {
-               emit(A64_MOVZ(is64, reg, lo, 0), ctx);
-               if (hi)
-                       emit(A64_MOVK(is64, reg, hi, 16), ctx);
-       }
-}
-
 static inline int bpf2a64_offset(int bpf_to, int bpf_from,
                                 const struct jit_ctx *ctx)
 {
@@ -163,7 +185,7 @@ static inline int epilogue_offset(const struct jit_ctx *ctx)
 /* Tail call offset to jump into */
 #define PROLOGUE_OFFSET 7
 
-static int build_prologue(struct jit_ctx *ctx)
+static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
 {
        const struct bpf_prog *prog = ctx->prog;
        const u8 r6 = bpf2a64[BPF_REG_6];
@@ -188,7 +210,7 @@ static int build_prologue(struct jit_ctx *ctx)
         *                        | ... | BPF prog stack
         *                        |     |
         *                        +-----+ <= (BPF_FP - prog->aux->stack_depth)
-        *                        |RSVD | JIT scratchpad
+        *                        |RSVD | padding
         * current A64_SP =>      +-----+ <= (BPF_FP - ctx->stack_size)
         *                        |     |
         *                        | ... | Function call stack
@@ -210,19 +232,19 @@ static int build_prologue(struct jit_ctx *ctx)
        /* Set up BPF prog stack base register */
        emit(A64_MOV(1, fp, A64_SP), ctx);
 
-       /* Initialize tail_call_cnt */
-       emit(A64_MOVZ(1, tcc, 0, 0), ctx);
+       if (!ebpf_from_cbpf) {
+               /* Initialize tail_call_cnt */
+               emit(A64_MOVZ(1, tcc, 0, 0), ctx);
 
-       cur_offset = ctx->idx - idx0;
-       if (cur_offset != PROLOGUE_OFFSET) {
-               pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n",
-                           cur_offset, PROLOGUE_OFFSET);
-               return -1;
+               cur_offset = ctx->idx - idx0;
+               if (cur_offset != PROLOGUE_OFFSET) {
+                       pr_err_once("PROLOGUE_OFFSET = %d, expected %d!\n",
+                                   cur_offset, PROLOGUE_OFFSET);
+                       return -1;
+               }
        }
 
-       /* 4 byte extra for skb_copy_bits buffer */
-       ctx->stack_size = prog->aux->stack_depth + 4;
-       ctx->stack_size = STACK_ALIGN(ctx->stack_size);
+       ctx->stack_size = STACK_ALIGN(prog->aux->stack_depth);
 
        /* Set up function call stack */
        emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx);
@@ -786,6 +808,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
        struct bpf_prog *tmp, *orig_prog = prog;
        struct bpf_binary_header *header;
        struct arm64_jit_data *jit_data;
+       bool was_classic = bpf_prog_was_classic(prog);
        bool tmp_blinded = false;
        bool extra_pass = false;
        struct jit_ctx ctx;
@@ -840,7 +863,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
                goto out_off;
        }
 
-       if (build_prologue(&ctx)) {
+       if (build_prologue(&ctx, was_classic)) {
                prog = orig_prog;
                goto out_off;
        }
@@ -863,7 +886,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
 skip_init_ctx:
        ctx.idx = 0;
 
-       build_prologue(&ctx);
+       build_prologue(&ctx, was_classic);
 
        if (build_body(&ctx)) {
                bpf_jit_binary_free(header);
index b3043c0..aee8d7b 100644 (file)
@@ -18,9 +18,9 @@
 #define PORT(offset) (CKSEG1ADDR(AR7_REGS_UART0) + (4 * offset))
 #endif
 
-#if defined(CONFIG_MACH_JZ4740) || defined(CONFIG_MACH_JZ4780)
-#include <asm/mach-jz4740/base.h>
-#define PORT(offset) (CKSEG1ADDR(JZ4740_UART0_BASE_ADDR) + (4 * offset))
+#ifdef CONFIG_MACH_INGENIC
+#define INGENIC_UART0_BASE_ADDR        0x10030000
+#define PORT(offset) (CKSEG1ADDR(INGENIC_UART0_BASE_ADDR) + (4 * offset))
 #endif
 
 #ifdef CONFIG_CPU_XLR
index 9987e0e..69ca005 100644 (file)
@@ -1,4 +1,2 @@
 # SPDX-License-Identifier: GPL-2.0
 dtb-$(CONFIG_FIT_IMAGE_FDT_XILFPGA)    += nexys4ddr.dtb
-
-obj-y                          += $(patsubst %.dtb, %.dtb.o, $(dtb-y))
index b51432d..0dd0d5d 100644 (file)
@@ -16,3 +16,4 @@ all-$(CONFIG_MIPS_GENERIC)    := vmlinux.gz.itb
 its-y                                  := vmlinux.its.S
 its-$(CONFIG_FIT_IMAGE_FDT_BOSTON)     += board-boston.its.S
 its-$(CONFIG_FIT_IMAGE_FDT_NI169445)   += board-ni169445.its.S
+its-$(CONFIG_FIT_IMAGE_FDT_XILFPGA)    += board-xilfpga.its.S
index 0b23b1a..8d098b9 100644 (file)
@@ -463,7 +463,7 @@ static int fpr_get_msa(struct task_struct *target,
 /*
  * Copy the floating-point context to the supplied NT_PRFPREG buffer.
  * Choose the appropriate helper for general registers, and then copy
- * the FCSR register separately.
+ * the FCSR and FIR registers separately.
  */
 static int fpr_get(struct task_struct *target,
                   const struct user_regset *regset,
@@ -471,6 +471,7 @@ static int fpr_get(struct task_struct *target,
                   void *kbuf, void __user *ubuf)
 {
        const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
+       const int fir_pos = fcr31_pos + sizeof(u32);
        int err;
 
        if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
@@ -483,6 +484,12 @@ static int fpr_get(struct task_struct *target,
        err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
                                  &target->thread.fpu.fcr31,
                                  fcr31_pos, fcr31_pos + sizeof(u32));
+       if (err)
+               return err;
+
+       err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                 &boot_cpu_data.fpu_id,
+                                 fir_pos, fir_pos + sizeof(u32));
 
        return err;
 }
@@ -531,7 +538,8 @@ static int fpr_set_msa(struct task_struct *target,
 /*
  * Copy the supplied NT_PRFPREG buffer to the floating-point context.
  * Choose the appropriate helper for general registers, and then copy
- * the FCSR register separately.
+ * the FCSR register separately.  Ignore the incoming FIR register
+ * contents though, as the register is read-only.
  *
  * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0',
  * which is supposed to have been guaranteed by the kernel before
@@ -545,6 +553,7 @@ static int fpr_set(struct task_struct *target,
                   const void *kbuf, const void __user *ubuf)
 {
        const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
+       const int fir_pos = fcr31_pos + sizeof(u32);
        u32 fcr31;
        int err;
 
@@ -572,6 +581,11 @@ static int fpr_set(struct task_struct *target,
                ptrace_setfcr31(target, fcr31);
        }
 
+       if (count > 0)
+               err = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+                                               fir_pos,
+                                               fir_pos + sizeof(u32));
+
        return err;
 }
 
@@ -793,7 +807,7 @@ long arch_ptrace(struct task_struct *child, long request,
                        fregs = get_fpu_regs(child);
 
 #ifdef CONFIG_32BIT
-                       if (test_thread_flag(TIF_32BIT_FPREGS)) {
+                       if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
                                /*
                                 * The odd registers are actually the high
                                 * order bits of the values stored in the even
@@ -888,7 +902,7 @@ long arch_ptrace(struct task_struct *child, long request,
 
                        init_fp_ctx(child);
 #ifdef CONFIG_32BIT
-                       if (test_thread_flag(TIF_32BIT_FPREGS)) {
+                       if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
                                /*
                                 * The odd registers are actually the high
                                 * order bits of the values stored in the even
index 2b9260f..656a137 100644 (file)
@@ -99,7 +99,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
                                break;
                        }
                        fregs = get_fpu_regs(child);
-                       if (test_thread_flag(TIF_32BIT_FPREGS)) {
+                       if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
                                /*
                                 * The odd registers are actually the high
                                 * order bits of the values stored in the even
@@ -212,7 +212,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
                                       sizeof(child->thread.fpu));
                                child->thread.fpu.fcr31 = 0;
                        }
-                       if (test_thread_flag(TIF_32BIT_FPREGS)) {
+                       if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) {
                                /*
                                 * The odd registers are actually the high
                                 * order bits of the values stored in the even
index 2549fdd..0f725e9 100644 (file)
@@ -45,7 +45,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
        { "cache",        VCPU_STAT(cache_exits),        KVM_STAT_VCPU },
        { "signal",       VCPU_STAT(signal_exits),       KVM_STAT_VCPU },
        { "interrupt",    VCPU_STAT(int_exits),          KVM_STAT_VCPU },
-       { "cop_unsuable", VCPU_STAT(cop_unusable_exits), KVM_STAT_VCPU },
+       { "cop_unusable", VCPU_STAT(cop_unusable_exits), KVM_STAT_VCPU },
        { "tlbmod",       VCPU_STAT(tlbmod_exits),       KVM_STAT_VCPU },
        { "tlbmiss_ld",   VCPU_STAT(tlbmiss_ld_exits),   KVM_STAT_VCPU },
        { "tlbmiss_st",   VCPU_STAT(tlbmiss_st_exits),   KVM_STAT_VCPU },
index 6f534b2..e12dfa4 100644 (file)
@@ -851,9 +851,12 @@ static void r4k_dma_cache_wback_inv(unsigned long addr, unsigned long size)
        /*
         * Either no secondary cache or the available caches don't have the
         * subset property so we have to flush the primary caches
-        * explicitly
+        * explicitly.
+        * If we would need IPI to perform an INDEX-type operation, then
+        * we have to use the HIT-type alternative as IPI cannot be used
+        * here due to interrupts possibly being disabled.
         */
-       if (size >= dcache_size) {
+       if (!r4k_op_needs_ipi(R4K_INDEX) && size >= dcache_size) {
                r4k_blast_dcache();
        } else {
                R4600_HIT_CACHEOP_WAR_IMPL;
@@ -890,7 +893,7 @@ static void r4k_dma_cache_inv(unsigned long addr, unsigned long size)
                return;
        }
 
-       if (size >= dcache_size) {
+       if (!r4k_op_needs_ipi(R4K_INDEX) && size >= dcache_size) {
                r4k_blast_dcache();
        } else {
                R4600_HIT_CACHEOP_WAR_IMPL;
index 7ba7df9..aeb7b1b 100644 (file)
@@ -95,7 +95,6 @@ enum reg_val_type {
  * struct jit_ctx - JIT context
  * @skf:               The sk_filter
  * @stack_size:                eBPF stack size
- * @tmp_offset:                eBPF $sp offset to 8-byte temporary memory
  * @idx:               Instruction index
  * @flags:             JIT flags
  * @offsets:           Instruction offsets
@@ -105,7 +104,6 @@ enum reg_val_type {
 struct jit_ctx {
        const struct bpf_prog *skf;
        int stack_size;
-       int tmp_offset;
        u32 idx;
        u32 flags;
        u32 *offsets;
@@ -293,7 +291,6 @@ static int gen_int_prologue(struct jit_ctx *ctx)
        locals_size = (ctx->flags & EBPF_SEEN_FP) ? MAX_BPF_STACK : 0;
 
        stack_adjust += locals_size;
-       ctx->tmp_offset = locals_size;
 
        ctx->stack_size = stack_adjust;
 
@@ -399,7 +396,6 @@ static void gen_imm_to_reg(const struct bpf_insn *insn, int reg,
                emit_instr(ctx, lui, reg, upper >> 16);
                emit_instr(ctx, addiu, reg, reg, lower);
        }
-
 }
 
 static int gen_imm_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
@@ -547,28 +543,6 @@ static int gen_imm_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
        return 0;
 }
 
-static void * __must_check
-ool_skb_header_pointer(const struct sk_buff *skb, int offset,
-                      int len, void *buffer)
-{
-       return skb_header_pointer(skb, offset, len, buffer);
-}
-
-static int size_to_len(const struct bpf_insn *insn)
-{
-       switch (BPF_SIZE(insn->code)) {
-       case BPF_B:
-               return 1;
-       case BPF_H:
-               return 2;
-       case BPF_W:
-               return 4;
-       case BPF_DW:
-               return 8;
-       }
-       return 0;
-}
-
 static void emit_const_to_reg(struct jit_ctx *ctx, int dst, u64 value)
 {
        if (value >= 0xffffffffffff8000ull || value < 0x8000ull) {
index ee5a78a..e0e1c97 100644 (file)
@@ -268,7 +268,7 @@ static struct parisc_device *find_device_by_addr(unsigned long hpa)
  * Walks up the device tree looking for a device of the specified type.
  * If it finds it, it returns it.  If not, it returns NULL.
  */
-const struct parisc_device * __init
+const struct parisc_device *
 find_pa_parent_type(const struct parisc_device *padev, int type)
 {
        const struct device *dev = &padev->dev;
index 4065b5e..5e26dbe 100644 (file)
@@ -423,8 +423,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle)
 }
 
 #ifdef CONFIG_PROC_FS
-int __init
-setup_profiling_timer(unsigned int multiplier)
+int setup_profiling_timer(unsigned int multiplier)
 {
        return -EINVAL;
 }
index 1bceb95..5584247 100644 (file)
@@ -44,6 +44,10 @@ static ssize_t opal_nvram_read(char *buf, size_t count, loff_t *index)
        return count;
 }
 
+/*
+ * This can be called in the panic path with interrupts off, so use
+ * mdelay in that case.
+ */
 static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index)
 {
        s64 rc = OPAL_BUSY;
@@ -58,10 +62,16 @@ static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index)
        while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
                rc = opal_write_nvram(__pa(buf), count, off);
                if (rc == OPAL_BUSY_EVENT) {
-                       msleep(OPAL_BUSY_DELAY_MS);
+                       if (in_interrupt() || irqs_disabled())
+                               mdelay(OPAL_BUSY_DELAY_MS);
+                       else
+                               msleep(OPAL_BUSY_DELAY_MS);
                        opal_poll_events(NULL);
                } else if (rc == OPAL_BUSY) {
-                       msleep(OPAL_BUSY_DELAY_MS);
+                       if (in_interrupt() || irqs_disabled())
+                               mdelay(OPAL_BUSY_DELAY_MS);
+                       else
+                               msleep(OPAL_BUSY_DELAY_MS);
                }
        }
 
index 6176fe9..941d8cc 100644 (file)
@@ -261,9 +261,9 @@ CONFIG_IP_VS_NQ=m
 CONFIG_IP_VS_FTP=m
 CONFIG_IP_VS_PE_SIP=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_TABLES_IPV4=m
+CONFIG_NF_TABLES_IPV4=y
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
-CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_TABLES_ARP=y
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_IP_NF_IPTABLES=m
 CONFIG_IP_NF_MATCH_AH=m
@@ -284,7 +284,7 @@ CONFIG_IP_NF_ARPTABLES=m
 CONFIG_IP_NF_ARPFILTER=m
 CONFIG_IP_NF_ARP_MANGLE=m
 CONFIG_NF_CONNTRACK_IPV6=m
-CONFIG_NF_TABLES_IPV6=m
+CONFIG_NF_TABLES_IPV6=y
 CONFIG_NFT_CHAIN_ROUTE_IPV6=m
 CONFIG_NFT_CHAIN_NAT_IPV6=m
 CONFIG_IP6_NF_IPTABLES=m
@@ -305,7 +305,7 @@ CONFIG_IP6_NF_RAW=m
 CONFIG_IP6_NF_SECURITY=m
 CONFIG_IP6_NF_NAT=m
 CONFIG_IP6_NF_TARGET_MASQUERADE=m
-CONFIG_NF_TABLES_BRIDGE=m
+CONFIG_NF_TABLES_BRIDGE=y
 CONFIG_RDS=m
 CONFIG_RDS_RDMA=m
 CONFIG_RDS_TCP=m
@@ -604,7 +604,6 @@ CONFIG_DETECT_HUNG_TASK=y
 CONFIG_WQ_WATCHDOG=y
 CONFIG_PANIC_ON_OOPS=y
 CONFIG_DEBUG_TIMEKEEPING=y
-CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
 CONFIG_PROVE_LOCKING=y
 CONFIG_LOCK_STAT=y
 CONFIG_DEBUG_LOCKDEP=y
index c105bcc..eb6f75f 100644 (file)
@@ -259,9 +259,9 @@ CONFIG_IP_VS_NQ=m
 CONFIG_IP_VS_FTP=m
 CONFIG_IP_VS_PE_SIP=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_TABLES_IPV4=m
+CONFIG_NF_TABLES_IPV4=y
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
-CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_TABLES_ARP=y
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_IP_NF_IPTABLES=m
 CONFIG_IP_NF_MATCH_AH=m
@@ -282,7 +282,7 @@ CONFIG_IP_NF_ARPTABLES=m
 CONFIG_IP_NF_ARPFILTER=m
 CONFIG_IP_NF_ARP_MANGLE=m
 CONFIG_NF_CONNTRACK_IPV6=m
-CONFIG_NF_TABLES_IPV6=m
+CONFIG_NF_TABLES_IPV6=y
 CONFIG_NFT_CHAIN_ROUTE_IPV6=m
 CONFIG_NFT_CHAIN_NAT_IPV6=m
 CONFIG_IP6_NF_IPTABLES=m
@@ -303,7 +303,7 @@ CONFIG_IP6_NF_RAW=m
 CONFIG_IP6_NF_SECURITY=m
 CONFIG_IP6_NF_NAT=m
 CONFIG_IP6_NF_TARGET_MASQUERADE=m
-CONFIG_NF_TABLES_BRIDGE=m
+CONFIG_NF_TABLES_BRIDGE=y
 CONFIG_RDS=m
 CONFIG_RDS_RDMA=m
 CONFIG_RDS_TCP=m
index e8077f0..2bf01ba 100644 (file)
@@ -13,6 +13,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/nospec-insn.h>
 #include <asm/vx-insn.h>
 
 /* Vector register range containing CRC-32 constants */
@@ -67,6 +68,8 @@
 
 .previous
 
+       GEN_BR_THUNK %r14
+
 .text
 /*
  * The CRC-32 function(s) use these calling conventions:
@@ -203,6 +206,6 @@ ENTRY(crc32_be_vgfm_16)
 
 .Ldone:
        VLGVF   %r2,%v2,3
-       br      %r14
+       BR_EX   %r14
 
 .previous
index d8c67a5..7d6f568 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/nospec-insn.h>
 #include <asm/vx-insn.h>
 
 /* Vector register range containing CRC-32 constants */
@@ -76,6 +77,7 @@
 
 .previous
 
+       GEN_BR_THUNK %r14
 
 .text
 
@@ -264,6 +266,6 @@ crc32_le_vgfm_generic:
 
 .Ldone:
        VLGVF   %r2,%v2,2
-       br      %r14
+       BR_EX   %r14
 
 .previous
diff --git a/arch/s390/include/asm/nospec-insn.h b/arch/s390/include/asm/nospec-insn.h
new file mode 100644 (file)
index 0000000..a01f811
--- /dev/null
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_S390_NOSPEC_ASM_H
+#define _ASM_S390_NOSPEC_ASM_H
+
+#include <asm/alternative-asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/dwarf.h>
+
+#ifdef __ASSEMBLY__
+
+#ifdef CONFIG_EXPOLINE
+
+_LC_BR_R1 = __LC_BR_R1
+
+/*
+ * The expoline macros are used to create thunks in the same format
+ * as gcc generates them. The 'comdat' section flag makes sure that
+ * the various thunks are merged into a single copy.
+ */
+       .macro __THUNK_PROLOG_NAME name
+       .pushsection .text.\name,"axG",@progbits,\name,comdat
+       .globl \name
+       .hidden \name
+       .type \name,@function
+\name:
+       CFI_STARTPROC
+       .endm
+
+       .macro __THUNK_EPILOG
+       CFI_ENDPROC
+       .popsection
+       .endm
+
+       .macro __THUNK_PROLOG_BR r1,r2
+       __THUNK_PROLOG_NAME __s390x_indirect_jump_r\r2\()use_r\r1
+       .endm
+
+       .macro __THUNK_PROLOG_BC d0,r1,r2
+       __THUNK_PROLOG_NAME __s390x_indirect_branch_\d0\()_\r2\()use_\r1
+       .endm
+
+       .macro __THUNK_BR r1,r2
+       jg      __s390x_indirect_jump_r\r2\()use_r\r1
+       .endm
+
+       .macro __THUNK_BC d0,r1,r2
+       jg      __s390x_indirect_branch_\d0\()_\r2\()use_\r1
+       .endm
+
+       .macro __THUNK_BRASL r1,r2,r3
+       brasl   \r1,__s390x_indirect_jump_r\r3\()use_r\r2
+       .endm
+
+       .macro  __DECODE_RR expand,reg,ruse
+       .set __decode_fail,1
+       .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+       .ifc \reg,%r\r1
+       .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+       .ifc \ruse,%r\r2
+       \expand \r1,\r2
+       .set __decode_fail,0
+       .endif
+       .endr
+       .endif
+       .endr
+       .if __decode_fail == 1
+       .error "__DECODE_RR failed"
+       .endif
+       .endm
+
+       .macro  __DECODE_RRR expand,rsave,rtarget,ruse
+       .set __decode_fail,1
+       .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+       .ifc \rsave,%r\r1
+       .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+       .ifc \rtarget,%r\r2
+       .irp r3,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+       .ifc \ruse,%r\r3
+       \expand \r1,\r2,\r3
+       .set __decode_fail,0
+       .endif
+       .endr
+       .endif
+       .endr
+       .endif
+       .endr
+       .if __decode_fail == 1
+       .error "__DECODE_RRR failed"
+       .endif
+       .endm
+
+       .macro  __DECODE_DRR expand,disp,reg,ruse
+       .set __decode_fail,1
+       .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+       .ifc \reg,%r\r1
+       .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+       .ifc \ruse,%r\r2
+       \expand \disp,\r1,\r2
+       .set __decode_fail,0
+       .endif
+       .endr
+       .endif
+       .endr
+       .if __decode_fail == 1
+       .error "__DECODE_DRR failed"
+       .endif
+       .endm
+
+       .macro __THUNK_EX_BR reg,ruse
+       # Be very careful when adding instructions to this macro!
+       # The ALTERNATIVE replacement code has a .+10 which targets
+       # the "br \reg" after the code has been patched.
+#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
+       exrl    0,555f
+       j       .
+#else
+       .ifc \reg,%r1
+       ALTERNATIVE "ex %r0,_LC_BR_R1", ".insn ril,0xc60000000000,0,.+10", 35
+       j       .
+       .else
+       larl    \ruse,555f
+       ex      0,0(\ruse)
+       j       .
+       .endif
+#endif
+555:   br      \reg
+       .endm
+
+       .macro __THUNK_EX_BC disp,reg,ruse
+#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
+       exrl    0,556f
+       j       .
+#else
+       larl    \ruse,556f
+       ex      0,0(\ruse)
+       j       .
+#endif
+556:   b       \disp(\reg)
+       .endm
+
+       .macro GEN_BR_THUNK reg,ruse=%r1
+       __DECODE_RR __THUNK_PROLOG_BR,\reg,\ruse
+       __THUNK_EX_BR \reg,\ruse
+       __THUNK_EPILOG
+       .endm
+
+       .macro GEN_B_THUNK disp,reg,ruse=%r1
+       __DECODE_DRR __THUNK_PROLOG_BC,\disp,\reg,\ruse
+       __THUNK_EX_BC \disp,\reg,\ruse
+       __THUNK_EPILOG
+       .endm
+
+       .macro BR_EX reg,ruse=%r1
+557:   __DECODE_RR __THUNK_BR,\reg,\ruse
+       .pushsection .s390_indirect_branches,"a",@progbits
+       .long   557b-.
+       .popsection
+       .endm
+
+        .macro B_EX disp,reg,ruse=%r1
+558:   __DECODE_DRR __THUNK_BC,\disp,\reg,\ruse
+       .pushsection .s390_indirect_branches,"a",@progbits
+       .long   558b-.
+       .popsection
+       .endm
+
+       .macro BASR_EX rsave,rtarget,ruse=%r1
+559:   __DECODE_RRR __THUNK_BRASL,\rsave,\rtarget,\ruse
+       .pushsection .s390_indirect_branches,"a",@progbits
+       .long   559b-.
+       .popsection
+       .endm
+
+#else
+       .macro GEN_BR_THUNK reg,ruse=%r1
+       .endm
+
+       .macro GEN_B_THUNK disp,reg,ruse=%r1
+       .endm
+
+        .macro BR_EX reg,ruse=%r1
+       br      \reg
+       .endm
+
+        .macro B_EX disp,reg,ruse=%r1
+       b       \disp(\reg)
+       .endm
+
+       .macro BASR_EX rsave,rtarget,ruse=%r1
+       basr    \rsave,\rtarget
+       .endm
+#endif
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_S390_NOSPEC_ASM_H */
index e297bcf..6090670 100644 (file)
 
 int verify_sha256_digest(void);
 
+extern u64 kernel_entry;
+extern u64 kernel_type;
+
+extern u64 crash_start;
+extern u64 crash_size;
+
 #endif /* __ASSEMBLY__ */
 #endif /* _S390_PURGATORY_H_ */
index 84ea622..f92dd8e 100644 (file)
@@ -65,6 +65,7 @@ obj-y += nospec-branch.o
 
 extra-y                                += head.o head64.o vmlinux.lds
 
+obj-$(CONFIG_SYSFS)            += nospec-sysfs.o
 CFLAGS_REMOVE_nospec-branch.o  += $(CC_FLAGS_EXPOLINE)
 
 obj-$(CONFIG_MODULES)          += module.o
index eb2a5c0..11aea74 100644 (file)
@@ -181,6 +181,7 @@ int main(void)
        OFFSET(__LC_MACHINE_FLAGS, lowcore, machine_flags);
        OFFSET(__LC_PREEMPT_COUNT, lowcore, preempt_count);
        OFFSET(__LC_GMAP, lowcore, gmap);
+       OFFSET(__LC_BR_R1, lowcore, br_r1_trampoline);
        /* software defined ABI-relevant lowcore locations 0xe00 - 0xe20 */
        OFFSET(__LC_DUMP_REIPL, lowcore, ipib);
        /* hardware defined lowcore locations 0x1000 - 0x18ff */
index f6c5600..b65874b 100644 (file)
@@ -9,18 +9,22 @@
 
 #include <linux/linkage.h>
 #include <asm/asm-offsets.h>
+#include <asm/nospec-insn.h>
 #include <asm/ptrace.h>
 #include <asm/sigp.h>
 
+       GEN_BR_THUNK %r9
+       GEN_BR_THUNK %r14
+
 ENTRY(s390_base_mcck_handler)
        basr    %r13,0
 0:     lg      %r15,__LC_PANIC_STACK   # load panic stack
        aghi    %r15,-STACK_FRAME_OVERHEAD
        larl    %r1,s390_base_mcck_handler_fn
-       lg      %r1,0(%r1)
-       ltgr    %r1,%r1
+       lg      %r9,0(%r1)
+       ltgr    %r9,%r9
        jz      1f
-       basr    %r14,%r1
+       BASR_EX %r14,%r9
 1:     la      %r1,4095
        lmg     %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)
        lpswe   __LC_MCK_OLD_PSW
@@ -37,10 +41,10 @@ ENTRY(s390_base_ext_handler)
        basr    %r13,0
 0:     aghi    %r15,-STACK_FRAME_OVERHEAD
        larl    %r1,s390_base_ext_handler_fn
-       lg      %r1,0(%r1)
-       ltgr    %r1,%r1
+       lg      %r9,0(%r1)
+       ltgr    %r9,%r9
        jz      1f
-       basr    %r14,%r1
+       BASR_EX %r14,%r9
 1:     lmg     %r0,%r15,__LC_SAVE_AREA_ASYNC
        ni      __LC_EXT_OLD_PSW+1,0xfd # clear wait state bit
        lpswe   __LC_EXT_OLD_PSW
@@ -57,10 +61,10 @@ ENTRY(s390_base_pgm_handler)
        basr    %r13,0
 0:     aghi    %r15,-STACK_FRAME_OVERHEAD
        larl    %r1,s390_base_pgm_handler_fn
-       lg      %r1,0(%r1)
-       ltgr    %r1,%r1
+       lg      %r9,0(%r1)
+       ltgr    %r9,%r9
        jz      1f
-       basr    %r14,%r1
+       BASR_EX %r14,%r9
        lmg     %r0,%r15,__LC_SAVE_AREA_SYNC
        lpswe   __LC_PGM_OLD_PSW
 1:     lpswe   disabled_wait_psw-0b(%r13)
@@ -117,7 +121,7 @@ ENTRY(diag308_reset)
        larl    %r4,.Lcontinue_psw      # Restore PSW flags
        lpswe   0(%r4)
 .Lcontinue:
-       br      %r14
+       BR_EX   %r14
 .align 16
 .Lrestart_psw:
        .long   0x00080000,0x80000000 + .Lrestart_part2
index 3f22f13..f03402e 100644 (file)
@@ -28,6 +28,7 @@
 #include <asm/setup.h>
 #include <asm/nmi.h>
 #include <asm/export.h>
+#include <asm/nospec-insn.h>
 
 __PT_R0      = __PT_GPRS
 __PT_R1      = __PT_GPRS + 8
@@ -183,67 +184,9 @@ _LPP_OFFSET        = __LC_LPP
                    "jnz .+8; .long 0xb2e8d000", 82
        .endm
 
-#ifdef CONFIG_EXPOLINE
-
-       .macro GEN_BR_THUNK name,reg,tmp
-       .section .text.\name,"axG",@progbits,\name,comdat
-       .globl \name
-       .hidden \name
-       .type \name,@function
-\name:
-       CFI_STARTPROC
-#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
-       exrl    0,0f
-#else
-       larl    \tmp,0f
-       ex      0,0(\tmp)
-#endif
-       j       .
-0:     br      \reg
-       CFI_ENDPROC
-       .endm
-
-       GEN_BR_THUNK __s390x_indirect_jump_r1use_r9,%r9,%r1
-       GEN_BR_THUNK __s390x_indirect_jump_r1use_r14,%r14,%r1
-       GEN_BR_THUNK __s390x_indirect_jump_r11use_r14,%r14,%r11
-
-       .macro BASR_R14_R9
-0:     brasl   %r14,__s390x_indirect_jump_r1use_r9
-       .pushsection .s390_indirect_branches,"a",@progbits
-       .long   0b-.
-       .popsection
-       .endm
-
-       .macro BR_R1USE_R14
-0:     jg      __s390x_indirect_jump_r1use_r14
-       .pushsection .s390_indirect_branches,"a",@progbits
-       .long   0b-.
-       .popsection
-       .endm
-
-       .macro BR_R11USE_R14
-0:     jg      __s390x_indirect_jump_r11use_r14
-       .pushsection .s390_indirect_branches,"a",@progbits
-       .long   0b-.
-       .popsection
-       .endm
-
-#else  /* CONFIG_EXPOLINE */
-
-       .macro BASR_R14_R9
-       basr    %r14,%r9
-       .endm
-
-       .macro BR_R1USE_R14
-       br      %r14
-       .endm
-
-       .macro BR_R11USE_R14
-       br      %r14
-       .endm
-
-#endif /* CONFIG_EXPOLINE */
-
+       GEN_BR_THUNK %r9
+       GEN_BR_THUNK %r14
+       GEN_BR_THUNK %r14,%r11
 
        .section .kprobes.text, "ax"
 .Ldummy:
@@ -260,7 +203,7 @@ _LPP_OFFSET = __LC_LPP
 ENTRY(__bpon)
        .globl __bpon
        BPON
-       BR_R1USE_R14
+       BR_EX   %r14
 
 /*
  * Scheduler resume function, called by switch_to
@@ -284,7 +227,7 @@ ENTRY(__switch_to)
        mvc     __LC_CURRENT_PID(4,%r0),0(%r3)  # store pid of next
        lmg     %r6,%r15,__SF_GPRS(%r15)        # load gprs of next task
        ALTERNATIVE "", ".insn s,0xb2800000,_LPP_OFFSET", 40
-       BR_R1USE_R14
+       BR_EX   %r14
 
 .L__critical_start:
 
@@ -351,7 +294,7 @@ sie_exit:
        xgr     %r5,%r5
        lmg     %r6,%r14,__SF_GPRS(%r15)        # restore kernel registers
        lg      %r2,__SF_SIE_REASON(%r15)       # return exit reason code
-       BR_R1USE_R14
+       BR_EX   %r14
 .Lsie_fault:
        lghi    %r14,-EFAULT
        stg     %r14,__SF_SIE_REASON(%r15)      # set exit reason code
@@ -410,7 +353,7 @@ ENTRY(system_call)
        lgf     %r9,0(%r8,%r10)                 # get system call add.
        TSTMSK  __TI_flags(%r12),_TIF_TRACE
        jnz     .Lsysc_tracesys
-       BASR_R14_R9                             # call sys_xxxx
+       BASR_EX %r14,%r9                        # call sys_xxxx
        stg     %r2,__PT_R2(%r11)               # store return value
 
 .Lsysc_return:
@@ -595,7 +538,7 @@ ENTRY(system_call)
        lmg     %r3,%r7,__PT_R3(%r11)
        stg     %r7,STACK_FRAME_OVERHEAD(%r15)
        lg      %r2,__PT_ORIG_GPR2(%r11)
-       BASR_R14_R9                     # call sys_xxx
+       BASR_EX %r14,%r9                # call sys_xxx
        stg     %r2,__PT_R2(%r11)       # store return value
 .Lsysc_tracenogo:
        TSTMSK  __TI_flags(%r12),_TIF_TRACE
@@ -619,7 +562,7 @@ ENTRY(ret_from_fork)
        lmg     %r9,%r10,__PT_R9(%r11)  # load gprs
 ENTRY(kernel_thread_starter)
        la      %r2,0(%r10)
-       BASR_R14_R9
+       BASR_EX %r14,%r9
        j       .Lsysc_tracenogo
 
 /*
@@ -701,7 +644,7 @@ ENTRY(pgm_check_handler)
        je      .Lpgm_return
        lgf     %r9,0(%r10,%r1)         # load address of handler routine
        lgr     %r2,%r11                # pass pointer to pt_regs
-       BASR_R14_R9                     # branch to interrupt-handler
+       BASR_EX %r14,%r9                # branch to interrupt-handler
 .Lpgm_return:
        LOCKDEP_SYS_EXIT
        tm      __PT_PSW+1(%r11),0x01   # returning to user ?
@@ -1019,7 +962,7 @@ ENTRY(psw_idle)
        stpt    __TIMER_IDLE_ENTER(%r2)
 .Lpsw_idle_lpsw:
        lpswe   __SF_EMPTY(%r15)
-       BR_R1USE_R14
+       BR_EX   %r14
 .Lpsw_idle_end:
 
 /*
@@ -1061,7 +1004,7 @@ ENTRY(save_fpu_regs)
 .Lsave_fpu_regs_done:
        oi      __LC_CPU_FLAGS+7,_CIF_FPU
 .Lsave_fpu_regs_exit:
-       BR_R1USE_R14
+       BR_EX   %r14
 .Lsave_fpu_regs_end:
 EXPORT_SYMBOL(save_fpu_regs)
 
@@ -1107,7 +1050,7 @@ load_fpu_regs:
 .Lload_fpu_regs_done:
        ni      __LC_CPU_FLAGS+7,255-_CIF_FPU
 .Lload_fpu_regs_exit:
-       BR_R1USE_R14
+       BR_EX   %r14
 .Lload_fpu_regs_end:
 
 .L__critical_end:
@@ -1322,7 +1265,7 @@ cleanup_critical:
        jl      0f
        clg     %r9,BASED(.Lcleanup_table+104)  # .Lload_fpu_regs_end
        jl      .Lcleanup_load_fpu_regs
-0:     BR_R11USE_R14
+0:     BR_EX   %r14
 
        .align  8
 .Lcleanup_table:
@@ -1358,7 +1301,7 @@ cleanup_critical:
        ni      __SIE_PROG0C+3(%r9),0xfe        # no longer in SIE
        lctlg   %c1,%c1,__LC_USER_ASCE          # load primary asce
        larl    %r9,sie_exit                    # skip forward to sie_exit
-       BR_R11USE_R14
+       BR_EX   %r14
 #endif
 
 .Lcleanup_system_call:
@@ -1412,7 +1355,7 @@ cleanup_critical:
        stg     %r15,56(%r11)           # r15 stack pointer
        # set new psw address and exit
        larl    %r9,.Lsysc_do_svc
-       BR_R11USE_R14
+       BR_EX   %r14,%r11
 .Lcleanup_system_call_insn:
        .quad   system_call
        .quad   .Lsysc_stmg
@@ -1424,7 +1367,7 @@ cleanup_critical:
 
 .Lcleanup_sysc_tif:
        larl    %r9,.Lsysc_tif
-       BR_R11USE_R14
+       BR_EX   %r14,%r11
 
 .Lcleanup_sysc_restore:
        # check if stpt has been executed
@@ -1441,14 +1384,14 @@ cleanup_critical:
        mvc     0(64,%r11),__PT_R8(%r9)
        lmg     %r0,%r7,__PT_R0(%r9)
 1:     lmg     %r8,%r9,__LC_RETURN_PSW
-       BR_R11USE_R14
+       BR_EX   %r14,%r11
 .Lcleanup_sysc_restore_insn:
        .quad   .Lsysc_exit_timer
        .quad   .Lsysc_done - 4
 
 .Lcleanup_io_tif:
        larl    %r9,.Lio_tif
-       BR_R11USE_R14
+       BR_EX   %r14,%r11
 
 .Lcleanup_io_restore:
        # check if stpt has been executed
@@ -1462,7 +1405,7 @@ cleanup_critical:
        mvc     0(64,%r11),__PT_R8(%r9)
        lmg     %r0,%r7,__PT_R0(%r9)
 1:     lmg     %r8,%r9,__LC_RETURN_PSW
-       BR_R11USE_R14
+       BR_EX   %r14,%r11
 .Lcleanup_io_restore_insn:
        .quad   .Lio_exit_timer
        .quad   .Lio_done - 4
@@ -1515,17 +1458,17 @@ cleanup_critical:
        # prepare return psw
        nihh    %r8,0xfcfd              # clear irq & wait state bits
        lg      %r9,48(%r11)            # return from psw_idle
-       BR_R11USE_R14
+       BR_EX   %r14,%r11
 .Lcleanup_idle_insn:
        .quad   .Lpsw_idle_lpsw
 
 .Lcleanup_save_fpu_regs:
        larl    %r9,save_fpu_regs
-       BR_R11USE_R14
+       BR_EX   %r14,%r11
 
 .Lcleanup_load_fpu_regs:
        larl    %r9,load_fpu_regs
-       BR_R11USE_R14
+       BR_EX   %r14,%r11
 
 /*
  * Integer constants
index 94f2099..3d17c41 100644 (file)
@@ -176,10 +176,9 @@ void do_softirq_own_stack(void)
                new -= STACK_FRAME_OVERHEAD;
                ((struct stack_frame *) new)->back_chain = old;
                asm volatile("   la    15,0(%0)\n"
-                            "   basr  14,%2\n"
+                            "   brasl 14,__do_softirq\n"
                             "   la    15,0(%1)\n"
-                            : : "a" (new), "a" (old),
-                                "a" (__do_softirq)
+                            : : "a" (new), "a" (old)
                             : "0", "1", "2", "3", "4", "5", "14",
                               "cc", "memory" );
        } else {
index 82df7d8..27110f3 100644 (file)
@@ -9,13 +9,17 @@
 #include <linux/linkage.h>
 #include <asm/asm-offsets.h>
 #include <asm/ftrace.h>
+#include <asm/nospec-insn.h>
 #include <asm/ptrace.h>
 #include <asm/export.h>
 
+       GEN_BR_THUNK %r1
+       GEN_BR_THUNK %r14
+
        .section .kprobes.text, "ax"
 
 ENTRY(ftrace_stub)
-       br      %r14
+       BR_EX   %r14
 
 #define STACK_FRAME_SIZE  (STACK_FRAME_OVERHEAD + __PT_SIZE)
 #define STACK_PTREGS     (STACK_FRAME_OVERHEAD)
@@ -23,7 +27,7 @@ ENTRY(ftrace_stub)
 #define STACK_PTREGS_PSW  (STACK_PTREGS + __PT_PSW)
 
 ENTRY(_mcount)
-       br      %r14
+       BR_EX   %r14
 
 EXPORT_SYMBOL(_mcount)
 
@@ -53,7 +57,7 @@ ENTRY(ftrace_caller)
 #endif
        lgr     %r3,%r14
        la      %r5,STACK_PTREGS(%r15)
-       basr    %r14,%r1
+       BASR_EX %r14,%r1
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 # The j instruction gets runtime patched to a nop instruction.
 # See ftrace_enable_ftrace_graph_caller.
@@ -68,7 +72,7 @@ ftrace_graph_caller_end:
 #endif
        lg      %r1,(STACK_PTREGS_PSW+8)(%r15)
        lmg     %r2,%r15,(STACK_PTREGS_GPRS+2*8)(%r15)
-       br      %r1
+       BR_EX   %r1
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 
@@ -81,6 +85,6 @@ ENTRY(return_to_handler)
        aghi    %r15,STACK_FRAME_OVERHEAD
        lgr     %r14,%r2
        lmg     %r2,%r5,32(%r15)
-       br      %r14
+       BR_EX   %r14
 
 #endif
index 46d49a1..8ad6a71 100644 (file)
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/module.h>
 #include <linux/device.h>
-#include <linux/cpu.h>
 #include <asm/nospec-branch.h>
 
 static int __init nobp_setup_early(char *str)
@@ -44,24 +43,6 @@ static int __init nospec_report(void)
 }
 arch_initcall(nospec_report);
 
-#ifdef CONFIG_SYSFS
-ssize_t cpu_show_spectre_v1(struct device *dev,
-                           struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "Mitigation: __user pointer sanitization\n");
-}
-
-ssize_t cpu_show_spectre_v2(struct device *dev,
-                           struct device_attribute *attr, char *buf)
-{
-       if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
-               return sprintf(buf, "Mitigation: execute trampolines\n");
-       if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
-               return sprintf(buf, "Mitigation: limited branch prediction.\n");
-       return sprintf(buf, "Vulnerable\n");
-}
-#endif
-
 #ifdef CONFIG_EXPOLINE
 
 int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
@@ -112,7 +93,6 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end)
        s32 *epo;
 
        /* Second part of the instruction replace is always a nop */
-       memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x00, 0x00 }, 4);
        for (epo = start; epo < end; epo++) {
                instr = (u8 *) epo + *epo;
                if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04)
@@ -133,18 +113,34 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end)
                        br = thunk + (*(int *)(thunk + 2)) * 2;
                else
                        continue;
-               if (br[0] != 0x07 || (br[1] & 0xf0) != 0xf0)
+               /* Check for unconditional branch 0x07f? or 0x47f???? */
+               if ((br[0] & 0xbf) != 0x07 || (br[1] & 0xf0) != 0xf0)
                        continue;
+
+               memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x07, 0x00 }, 4);
                switch (type) {
                case BRCL_EXPOLINE:
-                       /* brcl to thunk, replace with br + nop */
                        insnbuf[0] = br[0];
                        insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
+                       if (br[0] == 0x47) {
+                               /* brcl to b, replace with bc + nopr */
+                               insnbuf[2] = br[2];
+                               insnbuf[3] = br[3];
+                       } else {
+                               /* brcl to br, replace with bcr + nop */
+                       }
                        break;
                case BRASL_EXPOLINE:
-                       /* brasl to thunk, replace with basr + nop */
-                       insnbuf[0] = 0x0d;
                        insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f);
+                       if (br[0] == 0x47) {
+                               /* brasl to b, replace with bas + nopr */
+                               insnbuf[0] = 0x4d;
+                               insnbuf[2] = br[2];
+                               insnbuf[3] = br[3];
+                       } else {
+                               /* brasl to br, replace with basr + nop */
+                               insnbuf[0] = 0x0d;
+                       }
                        break;
                }
 
diff --git a/arch/s390/kernel/nospec-sysfs.c b/arch/s390/kernel/nospec-sysfs.c
new file mode 100644 (file)
index 0000000..8affad5
--- /dev/null
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/device.h>
+#include <linux/cpu.h>
+#include <asm/facility.h>
+#include <asm/nospec-branch.h>
+
+ssize_t cpu_show_spectre_v1(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "Mitigation: __user pointer sanitization\n");
+}
+
+ssize_t cpu_show_spectre_v2(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
+               return sprintf(buf, "Mitigation: execute trampolines\n");
+       if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
+               return sprintf(buf, "Mitigation: limited branch prediction\n");
+       return sprintf(buf, "Vulnerable\n");
+}
index 1c9ddd7..0292d68 100644 (file)
@@ -753,6 +753,10 @@ static int __hw_perf_event_init(struct perf_event *event)
         */
        rate = 0;
        if (attr->freq) {
+               if (!attr->sample_freq) {
+                       err = -EINVAL;
+                       goto out;
+               }
                rate = freq_to_sample_rate(&si, attr->sample_freq);
                rate = hw_limit_rate(&si, rate);
                attr->freq = 0;
index 73cc375..7f14adf 100644 (file)
@@ -7,8 +7,11 @@
 
 #include <linux/linkage.h>
 #include <asm/asm-offsets.h>
+#include <asm/nospec-insn.h>
 #include <asm/sigp.h>
 
+       GEN_BR_THUNK %r9
+
 #
 # Issue "store status" for the current CPU to its prefix page
 # and call passed function afterwards
@@ -67,9 +70,9 @@ ENTRY(store_status)
        st      %r4,0(%r1)
        st      %r5,4(%r1)
        stg     %r2,8(%r1)
-       lgr     %r1,%r2
+       lgr     %r9,%r2
        lgr     %r2,%r3
-       br      %r1
+       BR_EX   %r9
 
        .section .bss
        .align  8
index e991871..a049a7b 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/ptrace.h>
 #include <asm/thread_info.h>
 #include <asm/asm-offsets.h>
+#include <asm/nospec-insn.h>
 #include <asm/sigp.h>
 
 /*
@@ -24,6 +25,8 @@
  * (see below) in the resume process.
  * This function runs with disabled interrupts.
  */
+       GEN_BR_THUNK %r14
+
        .section .text
 ENTRY(swsusp_arch_suspend)
        stmg    %r6,%r15,__SF_GPRS(%r15)
@@ -103,7 +106,7 @@ ENTRY(swsusp_arch_suspend)
        spx     0x318(%r1)
        lmg     %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
        lghi    %r2,0
-       br      %r14
+       BR_EX   %r14
 
 /*
  * Restore saved memory image to correct place and restore register context.
@@ -197,11 +200,10 @@ pgm_check_entry:
        larl    %r15,init_thread_union
        ahi     %r15,1<<(PAGE_SHIFT+THREAD_SIZE_ORDER)
        larl    %r2,.Lpanic_string
-       larl    %r3,sclp_early_printk
        lghi    %r1,0
        sam31
        sigp    %r1,%r0,SIGP_SET_ARCHITECTURE
-       basr    %r14,%r3
+       brasl   %r14,sclp_early_printk
        larl    %r3,.Ldisabled_wait_31
        lpsw    0(%r3)
 4:
@@ -267,7 +269,7 @@ restore_registers:
        /* Return 0 */
        lmg     %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
        lghi    %r2,0
-       br      %r14
+       BR_EX   %r14
 
        .section .data..nosave,"aw",@progbits
        .align  8
index 495c9c4..2311f15 100644 (file)
@@ -7,6 +7,9 @@
 
 #include <linux/linkage.h>
 #include <asm/export.h>
+#include <asm/nospec-insn.h>
+
+       GEN_BR_THUNK %r14
 
 /*
  * void *memmove(void *dest, const void *src, size_t n)
@@ -33,14 +36,14 @@ ENTRY(memmove)
 .Lmemmove_forward_remainder:
        larl    %r5,.Lmemmove_mvc
        ex      %r4,0(%r5)
-       br      %r14
+       BR_EX   %r14
 .Lmemmove_reverse:
        ic      %r0,0(%r4,%r3)
        stc     %r0,0(%r4,%r1)
        brctg   %r4,.Lmemmove_reverse
        ic      %r0,0(%r4,%r3)
        stc     %r0,0(%r4,%r1)
-       br      %r14
+       BR_EX   %r14
 .Lmemmove_mvc:
        mvc     0(1,%r1),0(%r3)
 EXPORT_SYMBOL(memmove)
@@ -77,7 +80,7 @@ ENTRY(memset)
 .Lmemset_clear_remainder:
        larl    %r3,.Lmemset_xc
        ex      %r4,0(%r3)
-       br      %r14
+       BR_EX   %r14
 .Lmemset_fill:
        cghi    %r4,1
        lgr     %r1,%r2
@@ -95,10 +98,10 @@ ENTRY(memset)
        stc     %r3,0(%r1)
        larl    %r5,.Lmemset_mvc
        ex      %r4,0(%r5)
-       br      %r14
+       BR_EX   %r14
 .Lmemset_fill_exit:
        stc     %r3,0(%r1)
-       br      %r14
+       BR_EX   %r14
 .Lmemset_xc:
        xc      0(1,%r1),0(%r1)
 .Lmemset_mvc:
@@ -121,7 +124,7 @@ ENTRY(memcpy)
 .Lmemcpy_remainder:
        larl    %r5,.Lmemcpy_mvc
        ex      %r4,0(%r5)
-       br      %r14
+       BR_EX   %r14
 .Lmemcpy_loop:
        mvc     0(256,%r1),0(%r3)
        la      %r1,256(%r1)
@@ -159,10 +162,10 @@ ENTRY(__memset\bits)
        \insn   %r3,0(%r1)
        larl    %r5,.L__memset_mvc\bits
        ex      %r4,0(%r5)
-       br      %r14
+       BR_EX   %r14
 .L__memset_exit\bits:
        \insn   %r3,0(%r2)
-       br      %r14
+       BR_EX   %r14
 .L__memset_mvc\bits:
        mvc     \bytes(1,%r1),0(%r1)
 .endm
index b020bea..d2db8ac 100644 (file)
@@ -25,6 +25,8 @@
 #include <linux/bpf.h>
 #include <asm/cacheflush.h>
 #include <asm/dis.h>
+#include <asm/facility.h>
+#include <asm/nospec-branch.h>
 #include <asm/set_memory.h>
 #include "bpf_jit.h"
 
@@ -41,6 +43,8 @@ struct bpf_jit {
        int base_ip;            /* Base address for literal pool */
        int ret0_ip;            /* Address of return 0 */
        int exit_ip;            /* Address of exit */
+       int r1_thunk_ip;        /* Address of expoline thunk for 'br %r1' */
+       int r14_thunk_ip;       /* Address of expoline thunk for 'br %r14' */
        int tail_call_start;    /* Tail call start offset */
        int labels[1];          /* Labels for local jumps */
 };
@@ -246,6 +250,19 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
        REG_SET_SEEN(b2);                                       \
 })
 
+#define EMIT6_PCREL_RILB(op, b, target)                                \
+({                                                             \
+       int rel = (target - jit->prg) / 2;                      \
+       _EMIT6(op | reg_high(b) << 16 | rel >> 16, rel & 0xffff);       \
+       REG_SET_SEEN(b);                                        \
+})
+
+#define EMIT6_PCREL_RIL(op, target)                            \
+({                                                             \
+       int rel = (target - jit->prg) / 2;                      \
+       _EMIT6(op | rel >> 16, rel & 0xffff);                   \
+})
+
 #define _EMIT6_IMM(op, imm)                                    \
 ({                                                             \
        unsigned int __imm = (imm);                             \
@@ -438,8 +455,45 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth)
        EMIT4(0xb9040000, REG_2, BPF_REG_0);
        /* Restore registers */
        save_restore_regs(jit, REGS_RESTORE, stack_depth);
+       if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) {
+               jit->r14_thunk_ip = jit->prg;
+               /* Generate __s390_indirect_jump_r14 thunk */
+               if (test_facility(35)) {
+                       /* exrl %r0,.+10 */
+                       EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10);
+               } else {
+                       /* larl %r1,.+14 */
+                       EMIT6_PCREL_RILB(0xc0000000, REG_1, jit->prg + 14);
+                       /* ex 0,0(%r1) */
+                       EMIT4_DISP(0x44000000, REG_0, REG_1, 0);
+               }
+               /* j . */
+               EMIT4_PCREL(0xa7f40000, 0);
+       }
        /* br %r14 */
        _EMIT2(0x07fe);
+
+       if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable &&
+           (jit->seen & SEEN_FUNC)) {
+               jit->r1_thunk_ip = jit->prg;
+               /* Generate __s390_indirect_jump_r1 thunk */
+               if (test_facility(35)) {
+                       /* exrl %r0,.+10 */
+                       EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10);
+                       /* j . */
+                       EMIT4_PCREL(0xa7f40000, 0);
+                       /* br %r1 */
+                       _EMIT2(0x07f1);
+               } else {
+                       /* larl %r1,.+14 */
+                       EMIT6_PCREL_RILB(0xc0000000, REG_1, jit->prg + 14);
+                       /* ex 0,S390_lowcore.br_r1_tampoline */
+                       EMIT4_DISP(0x44000000, REG_0, REG_0,
+                                  offsetof(struct lowcore, br_r1_trampoline));
+                       /* j . */
+                       EMIT4_PCREL(0xa7f40000, 0);
+               }
+       }
 }
 
 /*
@@ -935,8 +989,13 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i
                /* lg %w1,<d(imm)>(%l) */
                EMIT6_DISP_LH(0xe3000000, 0x0004, REG_W1, REG_0, REG_L,
                              EMIT_CONST_U64(func));
-               /* basr %r14,%w1 */
-               EMIT2(0x0d00, REG_14, REG_W1);
+               if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) {
+                       /* brasl %r14,__s390_indirect_jump_r1 */
+                       EMIT6_PCREL_RILB(0xc0050000, REG_14, jit->r1_thunk_ip);
+               } else {
+                       /* basr %r14,%w1 */
+                       EMIT2(0x0d00, REG_14, REG_W1);
+               }
                /* lgr %b0,%r2: load return value into %b0 */
                EMIT4(0xb9040000, BPF_REG_0, REG_2);
                break;
index 9f5918e..222785a 100644 (file)
@@ -894,7 +894,6 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
        const int i = insn - ctx->prog->insnsi;
        const s16 off = insn->off;
        const s32 imm = insn->imm;
-       u32 *func;
 
        if (insn->src_reg == BPF_REG_FP)
                ctx->saw_frame_pointer = true;
index 47d3eff..09f36c0 100644 (file)
@@ -163,7 +163,8 @@ __setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom)
        if (status != EFI_SUCCESS)
                goto free_struct;
 
-       memcpy(rom->romdata, pci->romimage, pci->romsize);
+       memcpy(rom->romdata, (void *)(unsigned long)pci->romimage,
+              pci->romsize);
        return status;
 
 free_struct:
@@ -269,7 +270,8 @@ __setup_efi_pci64(efi_pci_io_protocol_64 *pci, struct pci_setup_rom **__rom)
        if (status != EFI_SUCCESS)
                goto free_struct;
 
-       memcpy(rom->romdata, pci->romimage, pci->romsize);
+       memcpy(rom->romdata, (void *)(unsigned long)pci->romimage,
+              pci->romsize);
        return status;
 
 free_struct:
index fca012b..8169e8b 100644 (file)
@@ -305,6 +305,25 @@ ENTRY(startup_64)
        /* Set up the stack */
        leaq    boot_stack_end(%rbx), %rsp
 
+       /*
+        * paging_prepare() and cleanup_trampoline() below can have GOT
+        * references. Adjust the table with address we are running at.
+        *
+        * Zero RAX for adjust_got: the GOT was not adjusted before;
+        * there's no adjustment to undo.
+        */
+       xorq    %rax, %rax
+
+       /*
+        * Calculate the address the binary is loaded at and use it as
+        * a GOT adjustment.
+        */
+       call    1f
+1:     popq    %rdi
+       subq    $1b, %rdi
+
+       call    adjust_got
+
        /*
         * At this point we are in long mode with 4-level paging enabled,
         * but we might want to enable 5-level paging or vice versa.
@@ -370,10 +389,14 @@ trampoline_return:
        /*
         * cleanup_trampoline() would restore trampoline memory.
         *
+        * RDI is address of the page table to use instead of page table
+        * in trampoline memory (if required).
+        *
         * RSI holds real mode data and needs to be preserved across
         * this function call.
         */
        pushq   %rsi
+       leaq    top_pgtable(%rbx), %rdi
        call    cleanup_trampoline
        popq    %rsi
 
@@ -381,6 +404,21 @@ trampoline_return:
        pushq   $0
        popfq
 
+       /*
+        * Previously we've adjusted the GOT with address the binary was
+        * loaded at. Now we need to re-adjust for relocation address.
+        *
+        * Calculate the address the binary is loaded at, so that we can
+        * undo the previous GOT adjustment.
+        */
+       call    1f
+1:     popq    %rax
+       subq    $1b, %rax
+
+       /* The new adjustment is the relocation address */
+       movq    %rbx, %rdi
+       call    adjust_got
+
 /*
  * Copy the compressed kernel to the end of our buffer
  * where decompression in place becomes safe.
@@ -481,19 +519,6 @@ relocated:
        shrq    $3, %rcx
        rep     stosq
 
-/*
- * Adjust our own GOT
- */
-       leaq    _got(%rip), %rdx
-       leaq    _egot(%rip), %rcx
-1:
-       cmpq    %rcx, %rdx
-       jae     2f
-       addq    %rbx, (%rdx)
-       addq    $8, %rdx
-       jmp     1b
-2:
-       
 /*
  * Do the extraction, and jump to the new kernel..
  */
@@ -512,6 +537,27 @@ relocated:
  */
        jmp     *%rax
 
+/*
+ * Adjust the global offset table
+ *
+ * RAX is the previous adjustment of the table to undo (use 0 if it's the
+ * first time we touch GOT).
+ * RDI is the new adjustment to apply.
+ */
+adjust_got:
+       /* Walk through the GOT adding the address to the entries */
+       leaq    _got(%rip), %rdx
+       leaq    _egot(%rip), %rcx
+1:
+       cmpq    %rcx, %rdx
+       jae     2f
+       subq    %rax, (%rdx)    /* Undo previous adjustment */
+       addq    %rdi, (%rdx)    /* Apply the new adjustment */
+       addq    $8, %rdx
+       jmp     1b
+2:
+       ret
+
        .code32
 /*
  * This is the 32-bit trampoline that will be copied over to low memory.
@@ -649,3 +695,10 @@ boot_stack_end:
        .balign 4096
 pgtable:
        .fill BOOT_PGT_SIZE, 1, 0
+
+/*
+ * The page table is going to be used instead of page table in the trampoline
+ * memory.
+ */
+top_pgtable:
+       .fill PAGE_SIZE, 1, 0
index 32af1cb..a362fa0 100644 (file)
@@ -22,14 +22,6 @@ struct paging_config {
 /* Buffer to preserve trampoline memory */
 static char trampoline_save[TRAMPOLINE_32BIT_SIZE];
 
-/*
- * The page table is going to be used instead of page table in the trampoline
- * memory.
- *
- * It must not be in BSS as BSS is cleared after cleanup_trampoline().
- */
-static char top_pgtable[PAGE_SIZE] __aligned(PAGE_SIZE) __section(.data);
-
 /*
  * Trampoline address will be printed by extract_kernel() for debugging
  * purposes.
@@ -134,7 +126,7 @@ out:
        return paging_config;
 }
 
-void cleanup_trampoline(void)
+void cleanup_trampoline(void *pgtable)
 {
        void *trampoline_pgtable;
 
@@ -145,8 +137,8 @@ void cleanup_trampoline(void)
         * if it's there.
         */
        if ((void *)__native_read_cr3() == trampoline_pgtable) {
-               memcpy(top_pgtable, trampoline_pgtable, PAGE_SIZE);
-               native_write_cr3((unsigned long)top_pgtable);
+               memcpy(pgtable, trampoline_pgtable, PAGE_SIZE);
+               native_write_cr3((unsigned long)pgtable);
        }
 
        /* Restore trampoline memory */
diff --git a/arch/x86/entry/vdso/vdso32/vdso-fakesections.c b/arch/x86/entry/vdso/vdso32/vdso-fakesections.c
deleted file mode 100644 (file)
index 541468e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../vdso-fakesections.c"
index a6006e7..45b2b1c 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/cpu.h>
 #include <linux/bitops.h>
 #include <linux/device.h>
+#include <linux/nospec.h>
 
 #include <asm/apic.h>
 #include <asm/stacktrace.h>
@@ -304,17 +305,20 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
 
        config = attr->config;
 
-       cache_type = (config >>  0) & 0xff;
+       cache_type = (config >> 0) & 0xff;
        if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
                return -EINVAL;
+       cache_type = array_index_nospec(cache_type, PERF_COUNT_HW_CACHE_MAX);
 
        cache_op = (config >>  8) & 0xff;
        if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX)
                return -EINVAL;
+       cache_op = array_index_nospec(cache_op, PERF_COUNT_HW_CACHE_OP_MAX);
 
        cache_result = (config >> 16) & 0xff;
        if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
                return -EINVAL;
+       cache_result = array_index_nospec(cache_result, PERF_COUNT_HW_CACHE_RESULT_MAX);
 
        val = hw_cache_event_ids[cache_type][cache_op][cache_result];
 
@@ -421,6 +425,8 @@ int x86_setup_perfctr(struct perf_event *event)
        if (attr->config >= x86_pmu.max_events)
                return -EINVAL;
 
+       attr->config = array_index_nospec((unsigned long)attr->config, x86_pmu.max_events);
+
        /*
         * The generic map:
         */
index 9aca448..9f8084f 100644 (file)
@@ -92,6 +92,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/perf_event.h>
+#include <linux/nospec.h>
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
 #include "../perf_event.h"
@@ -302,6 +303,7 @@ static int cstate_pmu_event_init(struct perf_event *event)
        } else if (event->pmu == &cstate_pkg_pmu) {
                if (cfg >= PERF_CSTATE_PKG_EVENT_MAX)
                        return -EINVAL;
+               cfg = array_index_nospec((unsigned long)cfg, PERF_CSTATE_PKG_EVENT_MAX);
                if (!pkg_msr[cfg].attr)
                        return -EINVAL;
                event->hw.event_base = pkg_msr[cfg].msr;
index e7edf19..b4771a6 100644 (file)
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/perf_event.h>
+#include <linux/nospec.h>
 #include <asm/intel-family.h>
 
 enum perf_msr_id {
@@ -158,9 +159,6 @@ static int msr_event_init(struct perf_event *event)
        if (event->attr.type != event->pmu->type)
                return -ENOENT;
 
-       if (cfg >= PERF_MSR_EVENT_MAX)
-               return -EINVAL;
-
        /* unsupported modes and filters */
        if (event->attr.exclude_user   ||
            event->attr.exclude_kernel ||
@@ -171,6 +169,11 @@ static int msr_event_init(struct perf_event *event)
            event->attr.sample_period) /* no sampling */
                return -EINVAL;
 
+       if (cfg >= PERF_MSR_EVENT_MAX)
+               return -EINVAL;
+
+       cfg = array_index_nospec((unsigned long)cfg, PERF_MSR_EVENT_MAX);
+
        if (!msr[cfg].attr)
                return -EINVAL;
 
index b27da96..aced6c9 100644 (file)
@@ -140,6 +140,20 @@ extern void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int bit);
 
 #define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit)
 
+#if defined(__clang__) && !defined(CC_HAVE_ASM_GOTO)
+
+/*
+ * Workaround for the sake of BPF compilation which utilizes kernel
+ * headers, but clang does not support ASM GOTO and fails the build.
+ */
+#ifndef __BPF_TRACING__
+#warning "Compiler lacks ASM_GOTO support. Add -D __BPF_TRACING__ to your compiler arguments"
+#endif
+
+#define static_cpu_has(bit)            boot_cpu_has(bit)
+
+#else
+
 /*
  * Static testing of CPU features.  Used the same as boot_cpu_has().
  * These will statically patch the target code for additional
@@ -195,6 +209,7 @@ t_no:
                boot_cpu_has(bit) :                             \
                _static_cpu_has(bit)                            \
 )
+#endif
 
 #define cpu_has_bug(c, bit)            cpu_has(c, (bit))
 #define set_cpu_bug(c, bit)            set_cpu_cap(c, (bit))
index b3e32b0..c2c01f8 100644 (file)
@@ -208,4 +208,22 @@ static inline int insn_offset_immediate(struct insn *insn)
        return insn_offset_displacement(insn) + insn->displacement.nbytes;
 }
 
+#define POP_SS_OPCODE 0x1f
+#define MOV_SREG_OPCODE 0x8e
+
+/*
+ * Intel SDM Vol.3A 6.8.3 states;
+ * "Any single-step trap that would be delivered following the MOV to SS
+ * instruction or POP to SS instruction (because EFLAGS.TF is 1) is
+ * suppressed."
+ * This function returns true if @insn is MOV SS or POP SS. On these
+ * instructions, single stepping is suppressed.
+ */
+static inline int insn_masking_exception(struct insn *insn)
+{
+       return insn->opcode.bytes[0] == POP_SS_OPCODE ||
+               (insn->opcode.bytes[0] == MOV_SREG_OPCODE &&
+                X86_MODRM_REG(insn->modrm.bytes[0]) == 2);
+}
+
 #endif /* _ASM_X86_INSN_H */
index 57e3785..cf9911b 100644 (file)
@@ -193,7 +193,7 @@ static inline int init_new_context(struct task_struct *tsk,
 
 #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
        if (cpu_feature_enabled(X86_FEATURE_OSPKE)) {
-               /* pkey 0 is the default and always allocated */
+               /* pkey 0 is the default and allocated implicitly */
                mm->context.pkey_allocation_map = 0x1;
                /* -1 means unallocated or invalid */
                mm->context.execute_only_pkey = -1;
index 2cd344d..2f700a1 100644 (file)
@@ -301,9 +301,9 @@ do {                                                                        \
  *    jmp *%edx for x86_32
  */
 #ifdef CONFIG_RETPOLINE
-#ifdef CONFIG_X86_64
-# define RETPOLINE_RAX_BPF_JIT_SIZE    17
-# define RETPOLINE_RAX_BPF_JIT()                               \
+# ifdef CONFIG_X86_64
+#  define RETPOLINE_RAX_BPF_JIT_SIZE   17
+#  define RETPOLINE_RAX_BPF_JIT()                              \
 do {                                                           \
        EMIT1_off32(0xE8, 7);    /* callq do_rop */             \
        /* spec_trap: */                                        \
@@ -314,8 +314,8 @@ do {                                                                \
        EMIT4(0x48, 0x89, 0x04, 0x24); /* mov %rax,(%rsp) */    \
        EMIT1(0xC3);             /* retq */                     \
 } while (0)
-#else
-# define RETPOLINE_EDX_BPF_JIT()                               \
+# else /* !CONFIG_X86_64 */
+#  define RETPOLINE_EDX_BPF_JIT()                              \
 do {                                                           \
        EMIT1_off32(0xE8, 7);    /* call do_rop */              \
        /* spec_trap: */                                        \
@@ -326,17 +326,16 @@ do {                                                              \
        EMIT3(0x89, 0x14, 0x24); /* mov %edx,(%esp) */          \
        EMIT1(0xC3);             /* ret */                      \
 } while (0)
-#endif
+# endif
 #else /* !CONFIG_RETPOLINE */
-
-#ifdef CONFIG_X86_64
-# define RETPOLINE_RAX_BPF_JIT_SIZE    2
-# define RETPOLINE_RAX_BPF_JIT()                               \
-       EMIT2(0xFF, 0xE0);       /* jmp *%rax */
-#else
-# define RETPOLINE_EDX_BPF_JIT()                               \
-       EMIT2(0xFF, 0xE2) /* jmp *%edx */
-#endif
+# ifdef CONFIG_X86_64
+#  define RETPOLINE_RAX_BPF_JIT_SIZE   2
+#  define RETPOLINE_RAX_BPF_JIT()                              \
+       EMIT2(0xFF, 0xE0);       /* jmp *%rax */
+# else /* !CONFIG_X86_64 */
+#  define RETPOLINE_EDX_BPF_JIT()                              \
+       EMIT2(0xFF, 0xE2)        /* jmp *%edx */
+# endif
 #endif
 
 #endif /* _ASM_X86_NOSPEC_BRANCH_H_ */
index a0ba1ff..851c04b 100644 (file)
@@ -2,6 +2,8 @@
 #ifndef _ASM_X86_PKEYS_H
 #define _ASM_X86_PKEYS_H
 
+#define ARCH_DEFAULT_PKEY      0
+
 #define arch_max_pkey() (boot_cpu_has(X86_FEATURE_OSPKE) ? 16 : 1)
 
 extern int arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
@@ -15,7 +17,7 @@ extern int __execute_only_pkey(struct mm_struct *mm);
 static inline int execute_only_pkey(struct mm_struct *mm)
 {
        if (!boot_cpu_has(X86_FEATURE_OSPKE))
-               return 0;
+               return ARCH_DEFAULT_PKEY;
 
        return __execute_only_pkey(mm);
 }
@@ -49,13 +51,21 @@ bool mm_pkey_is_allocated(struct mm_struct *mm, int pkey)
 {
        /*
         * "Allocated" pkeys are those that have been returned
-        * from pkey_alloc().  pkey 0 is special, and never
-        * returned from pkey_alloc().
+        * from pkey_alloc() or pkey 0 which is allocated
+        * implicitly when the mm is created.
         */
-       if (pkey <= 0)
+       if (pkey < 0)
                return false;
        if (pkey >= arch_max_pkey())
                return false;
+       /*
+        * The exec-only pkey is set in the allocation map, but
+        * is not available to any of the user interfaces like
+        * mprotect_pkey().
+        */
+       if (pkey == mm->context.execute_only_pkey)
+               return false;
+
        return mm_pkey_allocation_map(mm) & (1U << pkey);
 }
 
index 4c851eb..0ede697 100644 (file)
@@ -29,7 +29,7 @@
 #define KVM_FEATURE_PV_TLB_FLUSH       9
 #define KVM_FEATURE_ASYNC_PF_VMEXIT    10
 
-#define KVM_HINTS_DEDICATED      0
+#define KVM_HINTS_REALTIME      0
 
 /* The last 8 bits are used to indicate how to interpret the flags field
  * in pvclock structure. If no bits are set, all flags are ignored.
index c88e0b1..b481b95 100644 (file)
 #include <asm/amd_nb.h>
 
 #define PCI_DEVICE_ID_AMD_17H_ROOT     0x1450
+#define PCI_DEVICE_ID_AMD_17H_M10H_ROOT        0x15d0
 #define PCI_DEVICE_ID_AMD_17H_DF_F3    0x1463
 #define PCI_DEVICE_ID_AMD_17H_DF_F4    0x1464
+#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F3 0x15eb
+#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F4 0x15ec
 
 /* Protect the PCI config register pairs used for SMN and DF indirect access. */
 static DEFINE_MUTEX(smn_mutex);
@@ -24,6 +27,7 @@ static u32 *flush_words;
 
 static const struct pci_device_id amd_root_ids[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_ROOT) },
+       { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_ROOT) },
        {}
 };
 
@@ -39,6 +43,7 @@ const struct pci_device_id amd_nb_misc_ids[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
+       { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
        {}
 };
@@ -51,6 +56,7 @@ static const struct pci_device_id amd_nb_link_ids[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) },
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F4) },
+       { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F4) },
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F4) },
        {}
 };
index 8b04234..7685444 100644 (file)
@@ -116,6 +116,7 @@ static void init_x2apic_ldr(void)
                        goto update;
        }
        cmsk = cluster_hotplug_mask;
+       cmsk->clusterid = cluster;
        cluster_hotplug_mask = NULL;
 update:
        this_cpu_write(cluster_masks, cmsk);
index f7666ee..c8e0388 100644 (file)
@@ -94,6 +94,11 @@ static struct smca_bank_name smca_names[] = {
        [SMCA_SMU]      = { "smu",              "System Management Unit" },
 };
 
+static u32 smca_bank_addrs[MAX_NR_BANKS][NR_BLOCKS] __ro_after_init =
+{
+       [0 ... MAX_NR_BANKS - 1] = { [0 ... NR_BLOCKS - 1] = -1 }
+};
+
 const char *smca_get_name(enum smca_bank_types t)
 {
        if (t >= N_SMCA_BANK_TYPES)
@@ -443,20 +448,26 @@ static u32 smca_get_block_address(unsigned int cpu, unsigned int bank,
        if (!block)
                return MSR_AMD64_SMCA_MCx_MISC(bank);
 
+       /* Check our cache first: */
+       if (smca_bank_addrs[bank][block] != -1)
+               return smca_bank_addrs[bank][block];
+
        /*
         * For SMCA enabled processors, BLKPTR field of the first MISC register
         * (MCx_MISC0) indicates presence of additional MISC regs set (MISC1-4).
         */
        if (rdmsr_safe_on_cpu(cpu, MSR_AMD64_SMCA_MCx_CONFIG(bank), &low, &high))
-               return addr;
+               goto out;
 
        if (!(low & MCI_CONFIG_MCAX))
-               return addr;
+               goto out;
 
        if (!rdmsr_safe_on_cpu(cpu, MSR_AMD64_SMCA_MCx_MISC(bank), &low, &high) &&
            (low & MASK_BLKPTR_LO))
-               return MSR_AMD64_SMCA_MCx_MISCy(bank, block - 1);
+               addr = MSR_AMD64_SMCA_MCx_MISCy(bank, block - 1);
 
+out:
+       smca_bank_addrs[bank][block] = addr;
        return addr;
 }
 
@@ -468,18 +479,6 @@ static u32 get_block_address(unsigned int cpu, u32 current_addr, u32 low, u32 hi
        if ((bank >= mca_cfg.banks) || (block >= NR_BLOCKS))
                return addr;
 
-       /* Get address from already initialized block. */
-       if (per_cpu(threshold_banks, cpu)) {
-               struct threshold_bank *bankp = per_cpu(threshold_banks, cpu)[bank];
-
-               if (bankp && bankp->blocks) {
-                       struct threshold_block *blockp = &bankp->blocks[block];
-
-                       if (blockp)
-                               return blockp->address;
-               }
-       }
-
        if (mce_flags.smca)
                return smca_get_block_address(cpu, bank, block);
 
index 0c408f8..2d29e47 100644 (file)
@@ -104,6 +104,12 @@ static bool __head check_la57_support(unsigned long physaddr)
 }
 #endif
 
+/* Code in __startup_64() can be relocated during execution, but the compiler
+ * doesn't have to generate PC-relative relocations when accessing globals from
+ * that function. Clang actually does not generate them, which leads to
+ * boot-time crashes. To work around this problem, every global pointer must
+ * be adjusted using fixup_pointer().
+ */
 unsigned long __head __startup_64(unsigned long physaddr,
                                  struct boot_params *bp)
 {
@@ -113,6 +119,7 @@ unsigned long __head __startup_64(unsigned long physaddr,
        p4dval_t *p4d;
        pudval_t *pud;
        pmdval_t *pmd, pmd_entry;
+       pteval_t *mask_ptr;
        bool la57;
        int i;
        unsigned int *next_pgt_ptr;
@@ -196,7 +203,8 @@ unsigned long __head __startup_64(unsigned long physaddr,
 
        pmd_entry = __PAGE_KERNEL_LARGE_EXEC & ~_PAGE_GLOBAL;
        /* Filter out unsupported __PAGE_KERNEL_* bits: */
-       pmd_entry &= __supported_pte_mask;
+       mask_ptr = fixup_pointer(&__supported_pte_mask, physaddr);
+       pmd_entry &= *mask_ptr;
        pmd_entry += sme_get_me_mask();
        pmd_entry +=  physaddr;
 
index 0715f82..6f4d423 100644 (file)
@@ -370,6 +370,10 @@ int __copy_instruction(u8 *dest, u8 *src, u8 *real, struct insn *insn)
        if (insn->opcode.bytes[0] == BREAKPOINT_INSTRUCTION)
                return 0;
 
+       /* We should not singlestep on the exception masking instructions */
+       if (insn_masking_exception(insn))
+               return 0;
+
 #ifdef CONFIG_X86_64
        /* Only x86_64 has RIP relative instructions */
        if (insn_rip_relative(insn)) {
index 7867417..5b2300b 100644 (file)
@@ -457,7 +457,7 @@ static void __init sev_map_percpu_data(void)
 static void __init kvm_smp_prepare_cpus(unsigned int max_cpus)
 {
        native_smp_prepare_cpus(max_cpus);
-       if (kvm_para_has_hint(KVM_HINTS_DEDICATED))
+       if (kvm_para_has_hint(KVM_HINTS_REALTIME))
                static_branch_disable(&virt_spin_lock_key);
 }
 
@@ -553,7 +553,7 @@ static void __init kvm_guest_init(void)
        }
 
        if (kvm_para_has_feature(KVM_FEATURE_PV_TLB_FLUSH) &&
-           !kvm_para_has_hint(KVM_HINTS_DEDICATED) &&
+           !kvm_para_has_hint(KVM_HINTS_REALTIME) &&
            kvm_para_has_feature(KVM_FEATURE_STEAL_TIME))
                pv_mmu_ops.flush_tlb_others = kvm_flush_tlb_others;
 
@@ -649,7 +649,7 @@ static __init int kvm_setup_pv_tlb_flush(void)
        int cpu;
 
        if (kvm_para_has_feature(KVM_FEATURE_PV_TLB_FLUSH) &&
-           !kvm_para_has_hint(KVM_HINTS_DEDICATED) &&
+           !kvm_para_has_hint(KVM_HINTS_REALTIME) &&
            kvm_para_has_feature(KVM_FEATURE_STEAL_TIME)) {
                for_each_possible_cpu(cpu) {
                        zalloc_cpumask_var_node(per_cpu_ptr(&__pv_tlb_mask, cpu),
@@ -745,7 +745,7 @@ void __init kvm_spinlock_init(void)
        if (!kvm_para_has_feature(KVM_FEATURE_PV_UNHALT))
                return;
 
-       if (kvm_para_has_hint(KVM_HINTS_DEDICATED))
+       if (kvm_para_has_hint(KVM_HINTS_REALTIME))
                return;
 
        __pv_init_lock_hash();
index 60cdec6..d1ab07e 100644 (file)
@@ -57,12 +57,17 @@ static void load_segments(void)
 static void machine_kexec_free_page_tables(struct kimage *image)
 {
        free_page((unsigned long)image->arch.pgd);
+       image->arch.pgd = NULL;
 #ifdef CONFIG_X86_PAE
        free_page((unsigned long)image->arch.pmd0);
+       image->arch.pmd0 = NULL;
        free_page((unsigned long)image->arch.pmd1);
+       image->arch.pmd1 = NULL;
 #endif
        free_page((unsigned long)image->arch.pte0);
+       image->arch.pte0 = NULL;
        free_page((unsigned long)image->arch.pte1);
+       image->arch.pte1 = NULL;
 }
 
 static int machine_kexec_alloc_page_tables(struct kimage *image)
@@ -79,7 +84,6 @@ static int machine_kexec_alloc_page_tables(struct kimage *image)
            !image->arch.pmd0 || !image->arch.pmd1 ||
 #endif
            !image->arch.pte0 || !image->arch.pte1) {
-               machine_kexec_free_page_tables(image);
                return -ENOMEM;
        }
        return 0;
index a5e55d8..6010449 100644 (file)
@@ -39,9 +39,13 @@ const struct kexec_file_ops * const kexec_file_loaders[] = {
 static void free_transition_pgtable(struct kimage *image)
 {
        free_page((unsigned long)image->arch.p4d);
+       image->arch.p4d = NULL;
        free_page((unsigned long)image->arch.pud);
+       image->arch.pud = NULL;
        free_page((unsigned long)image->arch.pmd);
+       image->arch.pmd = NULL;
        free_page((unsigned long)image->arch.pte);
+       image->arch.pte = NULL;
 }
 
 static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
@@ -91,7 +95,6 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
        set_pte(pte, pfn_pte(paddr >> PAGE_SHIFT, PAGE_KERNEL_EXEC_NOENC));
        return 0;
 err:
-       free_transition_pgtable(image);
        return result;
 }
 
index 4b100fe..12bb445 100644 (file)
@@ -542,6 +542,7 @@ void set_personality_64bit(void)
        clear_thread_flag(TIF_X32);
        /* Pretend that this comes from a 64bit execve */
        task_pt_regs(current)->orig_ax = __NR_execve;
+       current_thread_info()->status &= ~TS_COMPAT;
 
        /* Ensure the corresponding mm is not marked. */
        if (current->mm)
index 85c7ef2..c84bb53 100644 (file)
@@ -299,6 +299,10 @@ static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool
        if (is_prefix_bad(insn))
                return -ENOTSUPP;
 
+       /* We should not singlestep on the exception masking instructions */
+       if (insn_masking_exception(insn))
+               return -ENOTSUPP;
+
        if (x86_64)
                good_insns = good_insns_64;
        else
index 98618e3..5708e95 100644 (file)
@@ -1265,7 +1265,7 @@ static int kvm_hv_hypercall_complete_userspace(struct kvm_vcpu *vcpu)
        struct kvm_run *run = vcpu->run;
 
        kvm_hv_hypercall_set_result(vcpu, run->hyperv.u.hcall.result);
-       return 1;
+       return kvm_skip_emulated_instruction(vcpu);
 }
 
 static u16 kvm_hvcall_signal_event(struct kvm_vcpu *vcpu, bool fast, u64 param)
@@ -1296,8 +1296,10 @@ static u16 kvm_hvcall_signal_event(struct kvm_vcpu *vcpu, bool fast, u64 param)
        if (param & ~KVM_HYPERV_CONN_ID_MASK)
                return HV_STATUS_INVALID_HYPERCALL_INPUT;
 
-       /* conn_to_evt is protected by vcpu->kvm->srcu */
+       /* the eventfd is protected by vcpu->kvm->srcu, but conn_to_evt isn't */
+       rcu_read_lock();
        eventfd = idr_find(&vcpu->kvm->arch.hyperv.conn_to_evt, param);
+       rcu_read_unlock();
        if (!eventfd)
                return HV_STATUS_INVALID_PORT_ID;
 
index c766880..3f16965 100644 (file)
@@ -1494,6 +1494,12 @@ static inline bool cpu_has_vmx_vmfunc(void)
                SECONDARY_EXEC_ENABLE_VMFUNC;
 }
 
+static bool vmx_umip_emulated(void)
+{
+       return vmcs_config.cpu_based_2nd_exec_ctrl &
+               SECONDARY_EXEC_DESC;
+}
+
 static inline bool report_flexpriority(void)
 {
        return flexpriority_enabled;
@@ -4761,14 +4767,16 @@ static int vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
        else
                hw_cr4 |= KVM_PMODE_VM_CR4_ALWAYS_ON;
 
-       if ((cr4 & X86_CR4_UMIP) && !boot_cpu_has(X86_FEATURE_UMIP)) {
-               vmcs_set_bits(SECONDARY_VM_EXEC_CONTROL,
-                             SECONDARY_EXEC_DESC);
-               hw_cr4 &= ~X86_CR4_UMIP;
-       } else if (!is_guest_mode(vcpu) ||
-                  !nested_cpu_has2(get_vmcs12(vcpu), SECONDARY_EXEC_DESC))
-               vmcs_clear_bits(SECONDARY_VM_EXEC_CONTROL,
+       if (!boot_cpu_has(X86_FEATURE_UMIP) && vmx_umip_emulated()) {
+               if (cr4 & X86_CR4_UMIP) {
+                       vmcs_set_bits(SECONDARY_VM_EXEC_CONTROL,
                                SECONDARY_EXEC_DESC);
+                       hw_cr4 &= ~X86_CR4_UMIP;
+               } else if (!is_guest_mode(vcpu) ||
+                       !nested_cpu_has2(get_vmcs12(vcpu), SECONDARY_EXEC_DESC))
+                       vmcs_clear_bits(SECONDARY_VM_EXEC_CONTROL,
+                                       SECONDARY_EXEC_DESC);
+       }
 
        if (cr4 & X86_CR4_VMXE) {
                /*
@@ -9497,12 +9505,6 @@ static bool vmx_xsaves_supported(void)
                SECONDARY_EXEC_XSAVES;
 }
 
-static bool vmx_umip_emulated(void)
-{
-       return vmcs_config.cpu_based_2nd_exec_ctrl &
-               SECONDARY_EXEC_DESC;
-}
-
 static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx)
 {
        u32 exit_intr_info;
index 51ecd38..59371de 100644 (file)
@@ -114,7 +114,7 @@ module_param(ignore_msrs, bool, S_IRUGO | S_IWUSR);
 static bool __read_mostly report_ignored_msrs = true;
 module_param(report_ignored_msrs, bool, S_IRUGO | S_IWUSR);
 
-unsigned int min_timer_period_us = 500;
+unsigned int min_timer_period_us = 200;
 module_param(min_timer_period_us, uint, S_IRUGO | S_IWUSR);
 
 static bool __read_mostly kvmclock_periodic_sync = true;
@@ -843,7 +843,10 @@ EXPORT_SYMBOL_GPL(kvm_set_cr4);
 int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
 {
 #ifdef CONFIG_X86_64
-       cr3 &= ~CR3_PCID_INVD;
+       bool pcid_enabled = kvm_read_cr4_bits(vcpu, X86_CR4_PCIDE);
+
+       if (pcid_enabled)
+               cr3 &= ~CR3_PCID_INVD;
 #endif
 
        if (cr3 == kvm_read_cr3(vcpu) && !pdptrs_changed(vcpu)) {
@@ -6671,12 +6674,13 @@ void kvm_vcpu_deactivate_apicv(struct kvm_vcpu *vcpu)
 int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
 {
        unsigned long nr, a0, a1, a2, a3, ret;
-       int op_64_bit, r;
-
-       r = kvm_skip_emulated_instruction(vcpu);
+       int op_64_bit;
 
-       if (kvm_hv_hypercall_enabled(vcpu->kvm))
-               return kvm_hv_hypercall(vcpu);
+       if (kvm_hv_hypercall_enabled(vcpu->kvm)) {
+               if (!kvm_hv_hypercall(vcpu))
+                       return 0;
+               goto out;
+       }
 
        nr = kvm_register_read(vcpu, VCPU_REGS_RAX);
        a0 = kvm_register_read(vcpu, VCPU_REGS_RBX);
@@ -6697,7 +6701,7 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
 
        if (kvm_x86_ops->get_cpl(vcpu) != 0) {
                ret = -KVM_EPERM;
-               goto out;
+               goto out_error;
        }
 
        switch (nr) {
@@ -6717,12 +6721,14 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
                ret = -KVM_ENOSYS;
                break;
        }
-out:
+out_error:
        if (!op_64_bit)
                ret = (u32)ret;
        kvm_register_write(vcpu, VCPU_REGS_RAX, ret);
+
+out:
        ++vcpu->stat.hypercalls;
-       return r;
+       return kvm_skip_emulated_instruction(vcpu);
 }
 EXPORT_SYMBOL_GPL(kvm_emulate_hypercall);
 
index d7bc0ee..6e98e0a 100644 (file)
@@ -94,26 +94,27 @@ int __arch_override_mprotect_pkey(struct vm_area_struct *vma, int prot, int pkey
         */
        if (pkey != -1)
                return pkey;
-       /*
-        * Look for a protection-key-drive execute-only mapping
-        * which is now being given permissions that are not
-        * execute-only.  Move it back to the default pkey.
-        */
-       if (vma_is_pkey_exec_only(vma) &&
-           (prot & (PROT_READ|PROT_WRITE))) {
-               return 0;
-       }
+
        /*
         * The mapping is execute-only.  Go try to get the
         * execute-only protection key.  If we fail to do that,
         * fall through as if we do not have execute-only
-        * support.
+        * support in this mm.
         */
        if (prot == PROT_EXEC) {
                pkey = execute_only_pkey(vma->vm_mm);
                if (pkey > 0)
                        return pkey;
+       } else if (vma_is_pkey_exec_only(vma)) {
+               /*
+                * Protections are *not* PROT_EXEC, but the mapping
+                * is using the exec-only pkey.  This mapping was
+                * PROT_EXEC and will no longer be.  Move back to
+                * the default pkey.
+                */
+               return ARCH_DEFAULT_PKEY;
        }
+
        /*
         * This is a vanilla, non-pkey mprotect (or we failed to
         * setup execute-only), inherit the pkey from the VMA we
index d33e7db..2d76106 100644 (file)
@@ -42,13 +42,11 @@ xmaddr_t arbitrary_virt_to_machine(void *vaddr)
 }
 EXPORT_SYMBOL_GPL(arbitrary_virt_to_machine);
 
-static void xen_flush_tlb_all(void)
+static noinline void xen_flush_tlb_all(void)
 {
        struct mmuext_op *op;
        struct multicall_space mcs;
 
-       trace_xen_mmu_flush_tlb_all(0);
-
        preempt_disable();
 
        mcs = xen_mc_entry(sizeof(*op));
index 486c0a3..2c30cab 100644 (file)
@@ -1310,13 +1310,11 @@ unsigned long xen_read_cr2_direct(void)
        return this_cpu_read(xen_vcpu_info.arch.cr2);
 }
 
-static void xen_flush_tlb(void)
+static noinline void xen_flush_tlb(void)
 {
        struct mmuext_op *op;
        struct multicall_space mcs;
 
-       trace_xen_mmu_flush_tlb(0);
-
        preempt_disable();
 
        mcs = xen_mc_entry(sizeof(*op));
index 514aaf9..3825df9 100644 (file)
@@ -56,6 +56,10 @@ acpi_status acpi_ns_initialize_objects(void);
 
 acpi_status acpi_ns_initialize_devices(u32 flags);
 
+acpi_status
+acpi_ns_init_one_package(acpi_handle obj_handle,
+                        u32 level, void *context, void **return_value);
+
 /*
  * nsload -  Namespace loading
  */
index 99d92cb..f85c6f3 100644 (file)
@@ -174,6 +174,13 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
                return_ACPI_STATUS(status);
        }
 
+       /* Complete the initialization/resolution of package objects */
+
+       status = acpi_ns_walk_namespace(ACPI_TYPE_PACKAGE, ACPI_ROOT_OBJECT,
+                                       ACPI_UINT32_MAX, 0,
+                                       acpi_ns_init_one_package, NULL, NULL,
+                                       NULL);
+
        /* Parameter Data (optional) */
 
        if (parameter_node) {
@@ -430,6 +437,13 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
                return_ACPI_STATUS(status);
        }
 
+       /* Complete the initialization/resolution of package objects */
+
+       status = acpi_ns_walk_namespace(ACPI_TYPE_PACKAGE, ACPI_ROOT_OBJECT,
+                                       ACPI_UINT32_MAX, 0,
+                                       acpi_ns_init_one_package, NULL, NULL,
+                                       NULL);
+
        /* Store the ddb_handle into the Target operand */
 
        status = acpi_ex_store(ddb_handle, target, walk_state);
index 77f2b5f..d77257d 100644 (file)
@@ -240,6 +240,58 @@ error_exit:
        return_ACPI_STATUS(status);
 }
 
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_init_one_package
+ *
+ * PARAMETERS:  obj_handle      - Node
+ *              level           - Current nesting level
+ *              context         - Not used
+ *              return_value    - Not used
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Callback from acpi_walk_namespace. Invoked for every package
+ *              within the namespace. Used during dynamic load of an SSDT.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_init_one_package(acpi_handle obj_handle,
+                        u32 level, void *context, void **return_value)
+{
+       acpi_status status;
+       union acpi_operand_object *obj_desc;
+       struct acpi_namespace_node *node =
+           (struct acpi_namespace_node *)obj_handle;
+
+       obj_desc = acpi_ns_get_attached_object(node);
+       if (!obj_desc) {
+               return (AE_OK);
+       }
+
+       /* Exit if package is already initialized */
+
+       if (obj_desc->package.flags & AOPOBJ_DATA_VALID) {
+               return (AE_OK);
+       }
+
+       status = acpi_ds_get_package_arguments(obj_desc);
+       if (ACPI_FAILURE(status)) {
+               return (AE_OK);
+       }
+
+       status =
+           acpi_ut_walk_package_tree(obj_desc, NULL,
+                                     acpi_ds_init_package_element, NULL);
+       if (ACPI_FAILURE(status)) {
+               return (AE_OK);
+       }
+
+       obj_desc->package.flags |= AOPOBJ_DATA_VALID;
+       return (AE_OK);
+}
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ns_init_one_object
@@ -360,27 +412,11 @@ acpi_ns_init_one_object(acpi_handle obj_handle,
 
        case ACPI_TYPE_PACKAGE:
 
-               info->package_init++;
-               status = acpi_ds_get_package_arguments(obj_desc);
-               if (ACPI_FAILURE(status)) {
-                       break;
-               }
-
-               ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
-                                     "%s: Completing resolution of Package elements\n",
-                                     ACPI_GET_FUNCTION_NAME));
+               /* Complete the initialization/resolution of the package object */
 
-               /*
-                * Resolve all named references in package objects (and all
-                * sub-packages). This action has been deferred until the entire
-                * namespace has been loaded, in order to support external and
-                * forward references from individual package elements (05/2017).
-                */
-               status = acpi_ut_walk_package_tree(obj_desc, NULL,
-                                                  acpi_ds_init_package_element,
-                                                  NULL);
-
-               obj_desc->package.flags |= AOPOBJ_DATA_VALID;
+               info->package_init++;
+               status =
+                   acpi_ns_init_one_package(obj_handle, level, NULL, NULL);
                break;
 
        default:
index 010f5f5..f3c643a 100644 (file)
@@ -197,6 +197,7 @@ config BT_HCIUART_BCM
 config BT_HCIUART_QCA
        bool "Qualcomm Atheros protocol support"
        depends on BT_HCIUART
+       depends on BT_HCIUART_SERDEV
        select BT_HCIUART_H4
        select BT_QCA
        help
index 6659f11..99cde1f 100644 (file)
@@ -315,10 +315,12 @@ static int btbcm_read_info(struct hci_dev *hdev)
        return 0;
 }
 
-static const struct {
+struct bcm_subver_table {
        u16 subver;
        const char *name;
-} bcm_uart_subver_table[] = {
+};
+
+static const struct bcm_subver_table bcm_uart_subver_table[] = {
        { 0x4103, "BCM4330B1"   },      /* 002.001.003 */
        { 0x410e, "BCM43341B0"  },      /* 002.001.014 */
        { 0x4406, "BCM4324B3"   },      /* 002.004.006 */
@@ -330,12 +332,28 @@ static const struct {
        { }
 };
 
-int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len)
+static const struct bcm_subver_table bcm_usb_subver_table[] = {
+       { 0x210b, "BCM43142A0"  },      /* 001.001.011 */
+       { 0x2112, "BCM4314A0"   },      /* 001.001.018 */
+       { 0x2118, "BCM20702A0"  },      /* 001.001.024 */
+       { 0x2126, "BCM4335A0"   },      /* 001.001.038 */
+       { 0x220e, "BCM20702A1"  },      /* 001.002.014 */
+       { 0x230f, "BCM4354A2"   },      /* 001.003.015 */
+       { 0x4106, "BCM4335B0"   },      /* 002.001.006 */
+       { 0x410e, "BCM20702B0"  },      /* 002.001.014 */
+       { 0x6109, "BCM4335C0"   },      /* 003.001.009 */
+       { 0x610c, "BCM4354"     },      /* 003.001.012 */
+       { }
+};
+
+int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len,
+                    bool reinit)
 {
-       u16 subver, rev;
-       const char *hw_name = NULL;
+       u16 subver, rev, pid, vid;
+       const char *hw_name = "BCM";
        struct sk_buff *skb;
        struct hci_rp_read_local_version *ver;
+       const struct bcm_subver_table *bcm_subver_table;
        int i, err;
 
        /* Reset */
@@ -354,30 +372,44 @@ int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len)
        kfree_skb(skb);
 
        /* Read controller information */
-       err = btbcm_read_info(hdev);
-       if (err)
-               return err;
+       if (!reinit) {
+               err = btbcm_read_info(hdev);
+               if (err)
+                       return err;
+       }
 
-       switch ((rev & 0xf000) >> 12) {
-       case 0:
-       case 1:
-       case 2:
-       case 3:
-               for (i = 0; bcm_uart_subver_table[i].name; i++) {
-                       if (subver == bcm_uart_subver_table[i].subver) {
-                               hw_name = bcm_uart_subver_table[i].name;
-                               break;
-                       }
+       /* Upper nibble of rev should be between 0 and 3? */
+       if (((rev & 0xf000) >> 12) > 3)
+               return 0;
+
+       bcm_subver_table = (hdev->bus == HCI_USB) ? bcm_usb_subver_table :
+                                                   bcm_uart_subver_table;
+
+       for (i = 0; bcm_subver_table[i].name; i++) {
+               if (subver == bcm_subver_table[i].subver) {
+                       hw_name = bcm_subver_table[i].name;
+                       break;
                }
+       }
 
-               snprintf(fw_name, len, "brcm/%s.hcd", hw_name ? : "BCM");
-               break;
-       default:
-               return 0;
+       if (hdev->bus == HCI_USB) {
+               /* Read USB Product Info */
+               skb = btbcm_read_usb_product(hdev);
+               if (IS_ERR(skb))
+                       return PTR_ERR(skb);
+
+               vid = get_unaligned_le16(skb->data + 1);
+               pid = get_unaligned_le16(skb->data + 3);
+               kfree_skb(skb);
+
+               snprintf(fw_name, len, "brcm/%s-%4.4x-%4.4x.hcd",
+                        hw_name, vid, pid);
+       } else {
+               snprintf(fw_name, len, "brcm/%s.hcd", hw_name);
        }
 
        bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u",
-                   hw_name ? : "BCM", (subver & 0xe000) >> 13,
+                   hw_name, (subver & 0xe000) >> 13,
                    (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
 
        return 0;
@@ -386,30 +418,14 @@ EXPORT_SYMBOL_GPL(btbcm_initialize);
 
 int btbcm_finalize(struct hci_dev *hdev)
 {
-       struct sk_buff *skb;
-       struct hci_rp_read_local_version *ver;
-       u16 subver, rev;
+       char fw_name[64];
        int err;
 
-       /* Reset */
-       err = btbcm_reset(hdev);
+       /* Re-initialize */
+       err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), true);
        if (err)
                return err;
 
-       /* Read Local Version Info */
-       skb = btbcm_read_local_version(hdev);
-       if (IS_ERR(skb))
-               return PTR_ERR(skb);
-
-       ver = (struct hci_rp_read_local_version *)skb->data;
-       rev = le16_to_cpu(ver->hci_rev);
-       subver = le16_to_cpu(ver->lmp_subver);
-       kfree_skb(skb);
-
-       bt_dev_info(hdev, "BCM (%3.3u.%3.3u.%3.3u) build %4.4u",
-                   (subver & 0xe000) >> 13, (subver & 0x1f00) >> 8,
-                   (subver & 0x00ff), rev & 0x0fff);
-
        btbcm_check_bdaddr(hdev);
 
        set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
@@ -418,95 +434,18 @@ int btbcm_finalize(struct hci_dev *hdev)
 }
 EXPORT_SYMBOL_GPL(btbcm_finalize);
 
-static const struct {
-       u16 subver;
-       const char *name;
-} bcm_usb_subver_table[] = {
-       { 0x210b, "BCM43142A0"  },      /* 001.001.011 */
-       { 0x2112, "BCM4314A0"   },      /* 001.001.018 */
-       { 0x2118, "BCM20702A0"  },      /* 001.001.024 */
-       { 0x2126, "BCM4335A0"   },      /* 001.001.038 */
-       { 0x220e, "BCM20702A1"  },      /* 001.002.014 */
-       { 0x230f, "BCM4354A2"   },      /* 001.003.015 */
-       { 0x4106, "BCM4335B0"   },      /* 002.001.006 */
-       { 0x410e, "BCM20702B0"  },      /* 002.001.014 */
-       { 0x6109, "BCM4335C0"   },      /* 003.001.009 */
-       { 0x610c, "BCM4354"     },      /* 003.001.012 */
-       { }
-};
-
 int btbcm_setup_patchram(struct hci_dev *hdev)
 {
        char fw_name[64];
        const struct firmware *fw;
-       u16 subver, rev, pid, vid;
-       const char *hw_name = NULL;
        struct sk_buff *skb;
-       struct hci_rp_read_local_version *ver;
-       int i, err;
-
-       /* Reset */
-       err = btbcm_reset(hdev);
-       if (err)
-               return err;
-
-       /* Read Local Version Info */
-       skb = btbcm_read_local_version(hdev);
-       if (IS_ERR(skb))
-               return PTR_ERR(skb);
-
-       ver = (struct hci_rp_read_local_version *)skb->data;
-       rev = le16_to_cpu(ver->hci_rev);
-       subver = le16_to_cpu(ver->lmp_subver);
-       kfree_skb(skb);
+       int err;
 
-       /* Read controller information */
-       err = btbcm_read_info(hdev);
+       /* Initialize */
+       err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), false);
        if (err)
                return err;
 
-       switch ((rev & 0xf000) >> 12) {
-       case 0:
-       case 3:
-               for (i = 0; bcm_uart_subver_table[i].name; i++) {
-                       if (subver == bcm_uart_subver_table[i].subver) {
-                               hw_name = bcm_uart_subver_table[i].name;
-                               break;
-                       }
-               }
-
-               snprintf(fw_name, sizeof(fw_name), "brcm/%s.hcd",
-                        hw_name ? : "BCM");
-               break;
-       case 1:
-       case 2:
-               /* Read USB Product Info */
-               skb = btbcm_read_usb_product(hdev);
-               if (IS_ERR(skb))
-                       return PTR_ERR(skb);
-
-               vid = get_unaligned_le16(skb->data + 1);
-               pid = get_unaligned_le16(skb->data + 3);
-               kfree_skb(skb);
-
-               for (i = 0; bcm_usb_subver_table[i].name; i++) {
-                       if (subver == bcm_usb_subver_table[i].subver) {
-                               hw_name = bcm_usb_subver_table[i].name;
-                               break;
-                       }
-               }
-
-               snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd",
-                        hw_name ? : "BCM", vid, pid);
-               break;
-       default:
-               return 0;
-       }
-
-       bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u",
-                   hw_name ? : "BCM", (subver & 0xe000) >> 13,
-                   (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
-
        err = request_firmware(&fw, fw_name, &hdev->dev);
        if (err < 0) {
                bt_dev_info(hdev, "BCM: Patch %s not found", fw_name);
@@ -517,25 +456,11 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
 
        release_firmware(fw);
 
-       /* Reset */
-       err = btbcm_reset(hdev);
+       /* Re-initialize */
+       err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), true);
        if (err)
                return err;
 
-       /* Read Local Version Info */
-       skb = btbcm_read_local_version(hdev);
-       if (IS_ERR(skb))
-               return PTR_ERR(skb);
-
-       ver = (struct hci_rp_read_local_version *)skb->data;
-       rev = le16_to_cpu(ver->hci_rev);
-       subver = le16_to_cpu(ver->lmp_subver);
-       kfree_skb(skb);
-
-       bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u",
-                   hw_name ? : "BCM", (subver & 0xe000) >> 13,
-                   (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
-
        /* Read Local Name */
        skb = btbcm_read_local_name(hdev);
        if (IS_ERR(skb))
index cfe6ad4..5346515 100644 (file)
@@ -73,7 +73,8 @@ int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw);
 int btbcm_setup_patchram(struct hci_dev *hdev);
 int btbcm_setup_apple(struct hci_dev *hdev);
 
-int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len);
+int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len,
+                    bool reinit);
 int btbcm_finalize(struct hci_dev *hdev);
 
 #else
@@ -104,7 +105,7 @@ static inline int btbcm_setup_apple(struct hci_dev *hdev)
 }
 
 static inline int btbcm_initialize(struct hci_dev *hdev, char *fw_name,
-                                  size_t len)
+                                  size_t len, bool reinit)
 {
        return 0;
 }
index 2793d41..8219816 100644 (file)
@@ -127,28 +127,41 @@ static void rome_tlv_check_data(struct rome_config *config,
        BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
        BT_DBG("Length\t\t : %d bytes", length);
 
+       config->dnld_mode = ROME_SKIP_EVT_NONE;
+
        switch (config->type) {
        case TLV_TYPE_PATCH:
                tlv_patch = (struct tlv_type_patch *)tlv->data;
-               BT_DBG("Total Length\t\t : %d bytes",
+
+               /* For Rome version 1.1 to 3.1, all segment commands
+                * are acked by a vendor specific event (VSE).
+                * For Rome >= 3.2, the download mode field indicates
+                * if VSE is skipped by the controller.
+                * In case VSE is skipped, only the last segment is acked.
+                */
+               config->dnld_mode = tlv_patch->download_mode;
+
+               BT_DBG("Total Length           : %d bytes",
                       le32_to_cpu(tlv_patch->total_size));
-               BT_DBG("Patch Data Length\t : %d bytes",
+               BT_DBG("Patch Data Length      : %d bytes",
                       le32_to_cpu(tlv_patch->data_length));
                BT_DBG("Signing Format Version : 0x%x",
                       tlv_patch->format_version);
-               BT_DBG("Signature Algorithm\t : 0x%x",
+               BT_DBG("Signature Algorithm    : 0x%x",
                       tlv_patch->signature);
-               BT_DBG("Reserved\t\t : 0x%x",
-                      le16_to_cpu(tlv_patch->reserved1));
-               BT_DBG("Product ID\t\t : 0x%04x",
+               BT_DBG("Download mode          : 0x%x",
+                      tlv_patch->download_mode);
+               BT_DBG("Reserved               : 0x%x",
+                      tlv_patch->reserved1);
+               BT_DBG("Product ID             : 0x%04x",
                       le16_to_cpu(tlv_patch->product_id));
-               BT_DBG("Rom Build Version\t : 0x%04x",
+               BT_DBG("Rom Build Version      : 0x%04x",
                       le16_to_cpu(tlv_patch->rom_build));
-               BT_DBG("Patch Version\t\t : 0x%04x",
+               BT_DBG("Patch Version          : 0x%04x",
                       le16_to_cpu(tlv_patch->patch_version));
-               BT_DBG("Reserved\t\t : 0x%x",
+               BT_DBG("Reserved               : 0x%x",
                       le16_to_cpu(tlv_patch->reserved2));
-               BT_DBG("Patch Entry Address\t : 0x%x",
+               BT_DBG("Patch Entry Address    : 0x%x",
                       le32_to_cpu(tlv_patch->entry));
                break;
 
@@ -194,8 +207,8 @@ static void rome_tlv_check_data(struct rome_config *config,
        }
 }
 
-static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size,
-                                const u8 *data)
+static int rome_tlv_send_segment(struct hci_dev *hdev, int seg_size,
+                                const u8 *data, enum rome_tlv_dnld_mode mode)
 {
        struct sk_buff *skb;
        struct edl_event_hdr *edl;
@@ -203,12 +216,14 @@ static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size,
        u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2];
        int err = 0;
 
-       BT_DBG("%s: Download segment #%d size %d", hdev->name, idx, seg_size);
-
        cmd[0] = EDL_PATCH_TLV_REQ_CMD;
        cmd[1] = seg_size;
        memcpy(cmd + 2, data, seg_size);
 
+       if (mode == ROME_SKIP_EVT_VSE_CC || mode == ROME_SKIP_EVT_VSE)
+               return __hci_cmd_send(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2,
+                                     cmd);
+
        skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,
                                HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
        if (IS_ERR(skb)) {
@@ -245,47 +260,12 @@ out:
        return err;
 }
 
-static int rome_tlv_download_request(struct hci_dev *hdev,
-                                    const struct firmware *fw)
-{
-       const u8 *buffer, *data;
-       int total_segment, remain_size;
-       int ret, i;
-
-       if (!fw || !fw->data)
-               return -EINVAL;
-
-       total_segment = fw->size / MAX_SIZE_PER_TLV_SEGMENT;
-       remain_size = fw->size % MAX_SIZE_PER_TLV_SEGMENT;
-
-       BT_DBG("%s: Total segment num %d remain size %d total size %zu",
-              hdev->name, total_segment, remain_size, fw->size);
-
-       data = fw->data;
-       for (i = 0; i < total_segment; i++) {
-               buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT;
-               ret = rome_tlv_send_segment(hdev, i, MAX_SIZE_PER_TLV_SEGMENT,
-                                           buffer);
-               if (ret < 0)
-                       return -EIO;
-       }
-
-       if (remain_size) {
-               buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT;
-               ret = rome_tlv_send_segment(hdev, total_segment, remain_size,
-                                           buffer);
-               if (ret < 0)
-                       return -EIO;
-       }
-
-       return 0;
-}
-
 static int rome_download_firmware(struct hci_dev *hdev,
                                  struct rome_config *config)
 {
        const struct firmware *fw;
-       int ret;
+       const u8 *segment;
+       int ret, remain, i = 0;
 
        bt_dev_info(hdev, "ROME Downloading %s", config->fwname);
 
@@ -298,10 +278,24 @@ static int rome_download_firmware(struct hci_dev *hdev,
 
        rome_tlv_check_data(config, fw);
 
-       ret = rome_tlv_download_request(hdev, fw);
-       if (ret) {
-               BT_ERR("%s: Failed to download file: %s (%d)", hdev->name,
-                      config->fwname, ret);
+       segment = fw->data;
+       remain = fw->size;
+       while (remain > 0) {
+               int segsize = min(MAX_SIZE_PER_TLV_SEGMENT, remain);
+
+               bt_dev_dbg(hdev, "Send segment %d, size %d", i++, segsize);
+
+               remain -= segsize;
+               /* The last segment is always acked regardless download mode */
+               if (!remain || segsize < MAX_SIZE_PER_TLV_SEGMENT)
+                       config->dnld_mode = ROME_SKIP_EVT_NONE;
+
+               ret = rome_tlv_send_segment(hdev, segsize, segment,
+                                           config->dnld_mode);
+               if (ret)
+                       break;
+
+               segment += segsize;
        }
 
        release_firmware(fw);
index 65e994b..13d77fd 100644 (file)
@@ -61,6 +61,13 @@ enum qca_bardrate {
        QCA_BAUDRATE_RESERVED
 };
 
+enum rome_tlv_dnld_mode {
+       ROME_SKIP_EVT_NONE,
+       ROME_SKIP_EVT_VSE,
+       ROME_SKIP_EVT_CC,
+       ROME_SKIP_EVT_VSE_CC
+};
+
 enum rome_tlv_type {
        TLV_TYPE_PATCH = 1,
        TLV_TYPE_NVM
@@ -70,6 +77,7 @@ struct rome_config {
        u8 type;
        char fwname[64];
        uint8_t user_baud_rate;
+       enum rome_tlv_dnld_mode dnld_mode;
 };
 
 struct edl_event_hdr {
@@ -94,7 +102,8 @@ struct tlv_type_patch {
        __le32 data_length;
        __u8   format_version;
        __u8   signature;
-       __le16 reserved1;
+       __u8   download_mode;
+       __u8   reserved1;
        __le16 product_id;
        __le16 rom_build;
        __le16 patch_version;
index 2c9a5fc..7df3eed 100644 (file)
@@ -65,6 +65,7 @@ static int btqcomsmd_cmd_callback(struct rpmsg_device *rpdev, void *data,
 {
        struct btqcomsmd *btq = priv;
 
+       btq->hdev->stat.byte_rx += count;
        return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
 }
 
@@ -76,12 +77,21 @@ static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
        switch (hci_skb_pkt_type(skb)) {
        case HCI_ACLDATA_PKT:
                ret = rpmsg_send(btq->acl_channel, skb->data, skb->len);
+               if (ret) {
+                       hdev->stat.err_tx++;
+                       break;
+               }
                hdev->stat.acl_tx++;
                hdev->stat.byte_tx += skb->len;
                break;
        case HCI_COMMAND_PKT:
                ret = rpmsg_send(btq->cmd_channel, skb->data, skb->len);
+               if (ret) {
+                       hdev->stat.err_tx++;
+                       break;
+               }
                hdev->stat.cmd_tx++;
+               hdev->stat.byte_tx += skb->len;
                break;
        default:
                ret = -EILSEQ;
index b937cc1..91882f5 100644 (file)
@@ -276,6 +276,8 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x04ca, 0x3011), .driver_info = BTUSB_QCA_ROME },
        { USB_DEVICE(0x04ca, 0x3015), .driver_info = BTUSB_QCA_ROME },
        { USB_DEVICE(0x04ca, 0x3016), .driver_info = BTUSB_QCA_ROME },
+       { USB_DEVICE(0x04ca, 0x301a), .driver_info = BTUSB_QCA_ROME },
+       { USB_DEVICE(0x13d3, 0x3496), .driver_info = BTUSB_QCA_ROME },
 
        /* Broadcom BCM2035 */
        { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
index 441f5e1..f06f0f1 100644 (file)
@@ -501,7 +501,7 @@ static int bcm_setup(struct hci_uart *hu)
        hu->hdev->set_diag = bcm_set_diag;
        hu->hdev->set_bdaddr = btbcm_set_bdaddr;
 
-       err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
+       err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name), false);
        if (err)
                return err;
 
@@ -794,19 +794,21 @@ static const struct acpi_gpio_mapping acpi_bcm_int_first_gpios[] = {
        { },
 };
 
-#ifdef CONFIG_ACPI
-/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */
-static const struct dmi_system_id bcm_active_low_irq_dmi_table[] = {
-       {       /* Handle ThinkPad 8 tablets with BCM2E55 chipset ACPI ID */
-               .ident = "Lenovo ThinkPad 8",
+/* Some firmware reports an IRQ which does not work (wrong pin in fw table?) */
+static const struct dmi_system_id bcm_broken_irq_dmi_table[] = {
+       {
+               .ident = "Meegopad T08",
                .matches = {
-                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                       DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
+                       DMI_EXACT_MATCH(DMI_BOARD_VENDOR,
+                                       "To be filled by OEM."),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "T3 MRD"),
+                       DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V1.1"),
                },
        },
        { }
 };
 
+#ifdef CONFIG_ACPI
 static int bcm_resource(struct acpi_resource *ares, void *data)
 {
        struct bcm_device *dev = data;
@@ -904,6 +906,8 @@ static int bcm_gpio_set_shutdown(struct bcm_device *dev, bool powered)
 
 static int bcm_get_resources(struct bcm_device *dev)
 {
+       const struct dmi_system_id *dmi_id;
+
        dev->name = dev_name(dev->dev);
 
        if (x86_apple_machine && !bcm_apple_get_resources(dev))
@@ -936,6 +940,13 @@ static int bcm_get_resources(struct bcm_device *dev)
                dev->irq = gpiod_to_irq(gpio);
        }
 
+       dmi_id = dmi_first_match(bcm_broken_irq_dmi_table);
+       if (dmi_id) {
+               dev_info(dev->dev, "%s: Has a broken IRQ config, disabling IRQ support / runtime-pm\n",
+                        dmi_id->ident);
+               dev->irq = 0;
+       }
+
        dev_dbg(dev->dev, "BCM irq: %d\n", dev->irq);
        return 0;
 }
@@ -944,7 +955,6 @@ static int bcm_get_resources(struct bcm_device *dev)
 static int bcm_acpi_probe(struct bcm_device *dev)
 {
        LIST_HEAD(resources);
-       const struct dmi_system_id *dmi_id;
        const struct acpi_gpio_mapping *gpio_mapping = acpi_bcm_int_last_gpios;
        struct resource_entry *entry;
        int ret;
@@ -991,13 +1001,6 @@ static int bcm_acpi_probe(struct bcm_device *dev)
                dev->irq_active_low = irq_polarity;
                dev_warn(dev->dev, "Overwriting IRQ polarity to active %s by module-param\n",
                         dev->irq_active_low ? "low" : "high");
-       } else {
-               dmi_id = dmi_first_match(bcm_active_low_irq_dmi_table);
-               if (dmi_id) {
-                       dev_warn(dev->dev, "%s: Overwriting IRQ polarity to active low",
-                                dmi_id->ident);
-                       dev->irq_active_low = true;
-               }
        }
 
        return 0;
index b6a7170..954213e 100644 (file)
@@ -447,6 +447,8 @@ static int hci_uart_setup(struct hci_dev *hdev)
                btbcm_check_bdaddr(hdev);
                break;
 #endif
+       default:
+               break;
        }
 
 done:
index 05ec530..f05382b 100644 (file)
  */
 
 #include <linux/kernel.h>
+#include <linux/clk.h>
 #include <linux/debugfs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/serdev.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -50,6 +55,9 @@
 #define IBS_TX_IDLE_TIMEOUT_MS         2000
 #define BAUDRATE_SETTLE_TIMEOUT_MS     300
 
+/* susclk rate */
+#define SUSCLK_RATE_32KHZ      32768
+
 /* HCI_IBS transmit side sleep protocol states */
 enum tx_ibs_states {
        HCI_IBS_TX_ASLEEP,
@@ -111,6 +119,12 @@ struct qca_data {
        u64 votes_off;
 };
 
+struct qca_serdev {
+       struct hci_uart  serdev_hu;
+       struct gpio_desc *bt_en;
+       struct clk       *susclk;
+};
+
 static void __serial_clock_on(struct tty_struct *tty)
 {
        /* TODO: Some chipset requires to enable UART clock on client
@@ -386,6 +400,7 @@ static void hci_ibs_wake_retrans_timeout(struct timer_list *t)
 /* Initialize protocol */
 static int qca_open(struct hci_uart *hu)
 {
+       struct qca_serdev *qcadev;
        struct qca_data *qca;
 
        BT_DBG("hu %p qca_open", hu);
@@ -444,6 +459,13 @@ static int qca_open(struct hci_uart *hu)
        timer_setup(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, 0);
        qca->tx_idle_delay = IBS_TX_IDLE_TIMEOUT_MS;
 
+       if (hu->serdev) {
+               serdev_device_open(hu->serdev);
+
+               qcadev = serdev_device_get_drvdata(hu->serdev);
+               gpiod_set_value_cansleep(qcadev->bt_en, 1);
+       }
+
        BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u",
               qca->tx_idle_delay, qca->wake_retrans);
 
@@ -512,6 +534,7 @@ static int qca_flush(struct hci_uart *hu)
 /* Close protocol */
 static int qca_close(struct hci_uart *hu)
 {
+       struct qca_serdev *qcadev;
        struct qca_data *qca = hu->priv;
 
        BT_DBG("hu %p qca close", hu);
@@ -525,6 +548,13 @@ static int qca_close(struct hci_uart *hu)
        destroy_workqueue(qca->workqueue);
        qca->hu = NULL;
 
+       if (hu->serdev) {
+               serdev_device_close(hu->serdev);
+
+               qcadev = serdev_device_get_drvdata(hu->serdev);
+               gpiod_set_value_cansleep(qcadev->bt_en, 0);
+       }
+
        kfree_skb(qca->rx_skb);
 
        hu->priv = NULL;
@@ -885,6 +915,14 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
        return 0;
 }
 
+static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)
+{
+       if (hu->serdev)
+               serdev_device_set_baudrate(hu->serdev, speed);
+       else
+               hci_uart_set_baudrate(hu, speed);
+}
+
 static int qca_setup(struct hci_uart *hu)
 {
        struct hci_dev *hdev = hu->hdev;
@@ -905,7 +943,7 @@ static int qca_setup(struct hci_uart *hu)
                speed = hu->proto->init_speed;
 
        if (speed)
-               hci_uart_set_baudrate(hu, speed);
+               host_set_baudrate(hu, speed);
 
        /* Setup user speed if needed */
        speed = 0;
@@ -924,7 +962,7 @@ static int qca_setup(struct hci_uart *hu)
                                   ret);
                        return ret;
                }
-               hci_uart_set_baudrate(hu, speed);
+               host_set_baudrate(hu, speed);
        }
 
        /* Setup patch / NVM configurations */
@@ -935,6 +973,12 @@ static int qca_setup(struct hci_uart *hu)
        } else if (ret == -ENOENT) {
                /* No patch/nvm-config found, run with original fw/config */
                ret = 0;
+       } else if (ret == -EAGAIN) {
+               /*
+                * Userspace firmware loader will return -EAGAIN in case no
+                * patch/nvm-config is found, so run with original fw/config.
+                */
+               ret = 0;
        }
 
        /* Setup bdaddr */
@@ -958,12 +1002,80 @@ static struct hci_uart_proto qca_proto = {
        .dequeue        = qca_dequeue,
 };
 
+static int qca_serdev_probe(struct serdev_device *serdev)
+{
+       struct qca_serdev *qcadev;
+       int err;
+
+       qcadev = devm_kzalloc(&serdev->dev, sizeof(*qcadev), GFP_KERNEL);
+       if (!qcadev)
+               return -ENOMEM;
+
+       qcadev->serdev_hu.serdev = serdev;
+       serdev_device_set_drvdata(serdev, qcadev);
+
+       qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable",
+                                      GPIOD_OUT_LOW);
+       if (IS_ERR(qcadev->bt_en)) {
+               dev_err(&serdev->dev, "failed to acquire enable gpio\n");
+               return PTR_ERR(qcadev->bt_en);
+       }
+
+       qcadev->susclk = devm_clk_get(&serdev->dev, NULL);
+       if (IS_ERR(qcadev->susclk)) {
+               dev_err(&serdev->dev, "failed to acquire clk\n");
+               return PTR_ERR(qcadev->susclk);
+       }
+
+       err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ);
+       if (err)
+               return err;
+
+       err = clk_prepare_enable(qcadev->susclk);
+       if (err)
+               return err;
+
+       err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
+       if (err)
+               clk_disable_unprepare(qcadev->susclk);
+
+       return err;
+}
+
+static void qca_serdev_remove(struct serdev_device *serdev)
+{
+       struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
+
+       hci_uart_unregister_device(&qcadev->serdev_hu);
+
+       clk_disable_unprepare(qcadev->susclk);
+}
+
+static const struct of_device_id qca_bluetooth_of_match[] = {
+       { .compatible = "qcom,qca6174-bt" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
+
+static struct serdev_device_driver qca_serdev_driver = {
+       .probe = qca_serdev_probe,
+       .remove = qca_serdev_remove,
+       .driver = {
+               .name = "hci_uart_qca",
+               .of_match_table = qca_bluetooth_of_match,
+       },
+};
+
 int __init qca_init(void)
 {
+       serdev_device_driver_register(&qca_serdev_driver);
+
        return hci_uart_register_proto(&qca_proto);
 }
 
 int __exit qca_deinit(void)
 {
+       serdev_device_driver_unregister(&qca_serdev_driver);
+
        return hci_uart_unregister_proto(&qca_proto);
 }
index 41492e9..34968a3 100644 (file)
@@ -266,15 +266,13 @@ config COMMON_CLK_STM32MP157
          Support for stm32mp157 SoC family clocks
 
 config COMMON_CLK_STM32F
-       bool "Clock driver for stm32f4 and stm32f7 SoC families"
-       depends on MACH_STM32F429 || MACH_STM32F469 || MACH_STM32F746
+       def_bool COMMON_CLK && (MACH_STM32F429 || MACH_STM32F469 || MACH_STM32F746)
        help
        ---help---
          Support for stm32f4 and stm32f7 SoC families clocks
 
 config COMMON_CLK_STM32H7
-       bool "Clock driver for stm32h7 SoC family"
-       depends on MACH_STM32H743
+       def_bool COMMON_CLK && MACH_STM32H743
        help
        ---help---
          Support for stm32h7 SoC family clocks
index 114ecbb..1232011 100644 (file)
@@ -464,7 +464,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
        clk_set_rate(clks[IMX6UL_CLK_AHB], 99000000);
 
        /* Change periph_pre clock to pll2_bus to adjust AXI rate to 264MHz */
-       clk_set_parent(clks[IMX6UL_CLK_PERIPH_CLK2_SEL], clks[IMX6UL_CLK_PLL3_USB_OTG]);
+       clk_set_parent(clks[IMX6UL_CLK_PERIPH_CLK2_SEL], clks[IMX6UL_CLK_OSC]);
        clk_set_parent(clks[IMX6UL_CLK_PERIPH], clks[IMX6UL_CLK_PERIPH_CLK2]);
        clk_set_parent(clks[IMX6UL_CLK_PERIPH_PRE], clks[IMX6UL_CLK_PLL2_BUS]);
        clk_set_parent(clks[IMX6UL_CLK_PERIPH], clks[IMX6UL_CLK_PERIPH_PRE]);
index de55c7d..96b35b8 100644 (file)
@@ -20,7 +20,7 @@ config ACPI_CPPC_CPUFREQ
 
 config ARM_ARMADA_37XX_CPUFREQ
        tristate "Armada 37xx CPUFreq support"
-       depends on ARCH_MVEBU
+       depends on ARCH_MVEBU && CPUFREQ_DT
        help
          This adds the CPUFreq driver support for Marvell Armada 37xx SoCs.
          The Armada 37xx PMU supports 4 frequency and VDD levels.
index d29275b..4a828c1 100644 (file)
@@ -524,6 +524,14 @@ static int bam_alloc_chan(struct dma_chan *chan)
        return 0;
 }
 
+static int bam_pm_runtime_get_sync(struct device *dev)
+{
+       if (pm_runtime_enabled(dev))
+               return pm_runtime_get_sync(dev);
+
+       return 0;
+}
+
 /**
  * bam_free_chan - Frees dma resources associated with specific channel
  * @chan: specified channel
@@ -539,7 +547,7 @@ static void bam_free_chan(struct dma_chan *chan)
        unsigned long flags;
        int ret;
 
-       ret = pm_runtime_get_sync(bdev->dev);
+       ret = bam_pm_runtime_get_sync(bdev->dev);
        if (ret < 0)
                return;
 
@@ -720,7 +728,7 @@ static int bam_pause(struct dma_chan *chan)
        unsigned long flag;
        int ret;
 
-       ret = pm_runtime_get_sync(bdev->dev);
+       ret = bam_pm_runtime_get_sync(bdev->dev);
        if (ret < 0)
                return ret;
 
@@ -746,7 +754,7 @@ static int bam_resume(struct dma_chan *chan)
        unsigned long flag;
        int ret;
 
-       ret = pm_runtime_get_sync(bdev->dev);
+       ret = bam_pm_runtime_get_sync(bdev->dev);
        if (ret < 0)
                return ret;
 
@@ -852,7 +860,7 @@ static irqreturn_t bam_dma_irq(int irq, void *data)
        if (srcs & P_IRQ)
                tasklet_schedule(&bdev->task);
 
-       ret = pm_runtime_get_sync(bdev->dev);
+       ret = bam_pm_runtime_get_sync(bdev->dev);
        if (ret < 0)
                return ret;
 
@@ -969,7 +977,7 @@ static void bam_start_dma(struct bam_chan *bchan)
        if (!vd)
                return;
 
-       ret = pm_runtime_get_sync(bdev->dev);
+       ret = bam_pm_runtime_get_sync(bdev->dev);
        if (ret < 0)
                return;
 
index 14b1471..2455be8 100644 (file)
@@ -778,6 +778,7 @@ scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
        if (scmi_mbox_chan_setup(info, &sdev->dev, prot_id)) {
                dev_err(&sdev->dev, "failed to setup transport\n");
                scmi_device_destroy(sdev);
+               return;
        }
 
        /* setup handle now as the transport is ready */
index b9bd827..1b4d465 100644 (file)
@@ -97,6 +97,16 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table_arg,
                u32 offset = !IS_ENABLED(CONFIG_DEBUG_ALIGN_RODATA) ?
                             (phys_seed >> 32) & mask : TEXT_OFFSET;
 
+               /*
+                * With CONFIG_RANDOMIZE_TEXT_OFFSET=y, TEXT_OFFSET may not
+                * be a multiple of EFI_KIMG_ALIGN, and we must ensure that
+                * we preserve the misalignment of 'offset' relative to
+                * EFI_KIMG_ALIGN so that statically allocated objects whose
+                * alignment exceeds PAGE_SIZE appear correctly aligned in
+                * memory.
+                */
+               offset |= TEXT_OFFSET % EFI_KIMG_ALIGN;
+
                /*
                 * If KASLR is enabled, and we have some randomness available,
                 * locate the kernel at a randomized offset in physical memory.
index a1b9338..c2c21d8 100644 (file)
@@ -716,7 +716,7 @@ static void remove_compat_control_link(struct drm_device *dev)
        if (!minor)
                return;
 
-       name = kasprintf(GFP_KERNEL, "controlD%d", minor->index);
+       name = kasprintf(GFP_KERNEL, "controlD%d", minor->index + 64);
        if (!name)
                return;
 
index 39ac15c..9e2ae02 100644 (file)
@@ -65,12 +65,13 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev,
                return -EINVAL;
 
        /* overflow checks for 32bit size calculations */
-       /* NOTE: DIV_ROUND_UP() can overflow */
+       if (args->bpp > U32_MAX - 8)
+               return -EINVAL;
        cpp = DIV_ROUND_UP(args->bpp, 8);
-       if (!cpp || cpp > 0xffffffffU / args->width)
+       if (cpp > U32_MAX / args->width)
                return -EINVAL;
        stride = cpp * args->width;
-       if (args->height > 0xffffffffU / stride)
+       if (args->height > U32_MAX / stride)
                return -EINVAL;
 
        /* test for wrap-around */
index e394799..6d9b945 100644 (file)
@@ -212,6 +212,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
                return -ENOMEM;
 
        filp->private_data = priv;
+       filp->f_mode |= FMODE_UNSIGNED_OFFSET;
        priv->filp = filp;
        priv->pid = get_pid(task_pid(current));
        priv->minor = minor;
index d596a83..854bd51 100644 (file)
@@ -778,6 +778,9 @@ i915_gem_userptr_ioctl(struct drm_device *dev,
                            I915_USERPTR_UNSYNCHRONIZED))
                return -EINVAL;
 
+       if (!args->user_size)
+               return -EINVAL;
+
        if (offset_in_page(args->user_ptr | args->user_size))
                return -EINVAL;
 
index e6a8c0e..8a69a92 100644 (file)
@@ -7326,6 +7326,9 @@ enum {
 #define SLICE_ECO_CHICKEN0                     _MMIO(0x7308)
 #define   PIXEL_MASK_CAMMING_DISABLE           (1 << 14)
 
+#define GEN9_WM_CHICKEN3                       _MMIO(0x5588)
+#define   GEN9_FACTOR_IN_CLR_VAL_HIZ           (1 << 9)
+
 /* WaCatErrorRejectionIssue */
 #define GEN7_SQ_CHICKEN_MBCUNIT_CONFIG         _MMIO(0x9030)
 #define  GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB      (1<<11)
index 4ba139c..f7c2582 100644 (file)
@@ -1149,6 +1149,10 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
        WA_SET_FIELD_MASKED(GEN8_CS_CHICKEN1, GEN9_PREEMPT_GPGPU_LEVEL_MASK,
                            GEN9_PREEMPT_GPGPU_COMMAND_LEVEL);
 
+       /* WaClearHIZ_WM_CHICKEN3:bxt,glk */
+       if (IS_GEN9_LP(dev_priv))
+               WA_SET_BIT_MASKED(GEN9_WM_CHICKEN3, GEN9_FACTOR_IN_CLR_VAL_HIZ);
+
        /* WaVFEStateAfterPipeControlwithMediaStateClear:skl,bxt,glk,cfl */
        ret = wa_ring_whitelist_reg(engine, GEN9_CTX_PREEMPT_REG);
        if (ret)
index e3a5f67..8704f7f 100644 (file)
@@ -884,6 +884,7 @@ static void execlists_submission_tasklet(unsigned long data)
 
                        head = execlists->csb_head;
                        tail = READ_ONCE(buf[write_idx]);
+                       rmb(); /* Hopefully paired with a wmb() in HW */
                }
                GEM_TRACE("%s cs-irq head=%d [%d%s], tail=%d [%d%s]\n",
                          engine->name,
index 94b99c9..7c95ed5 100644 (file)
@@ -130,6 +130,7 @@ static void vc4_close(struct drm_device *dev, struct drm_file *file)
        struct vc4_file *vc4file = file->driver_priv;
 
        vc4_perfmon_close_file(vc4file);
+       kfree(vc4file);
 }
 
 static const struct vm_operations_struct vc4_vm_ops = {
index 648f812..3d667e9 100644 (file)
@@ -482,6 +482,8 @@ vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,
                return ret;
        }
 
+       vps->dmabuf_size = size;
+
        /*
         * TTM already thinks the buffer is pinned, but make sure the
         * pin_count is upped.
index f249a44..6ec307c 100644 (file)
@@ -272,7 +272,7 @@ config SENSORS_K8TEMP
 
 config SENSORS_K10TEMP
        tristate "AMD Family 10h+ temperature sensor"
-       depends on X86 && PCI
+       depends on X86 && PCI && AMD_NB
        help
          If you say yes here you get support for the temperature
          sensor(s) inside your CPU. Supported are later revisions of
index d2cc55e..3b73dee 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <asm/amd_nb.h>
 #include <asm/processor.h>
 
 MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor");
@@ -40,8 +41,8 @@ static DEFINE_MUTEX(nb_smu_ind_mutex);
 #define PCI_DEVICE_ID_AMD_17H_DF_F3    0x1463
 #endif
 
-#ifndef PCI_DEVICE_ID_AMD_17H_RR_NB
-#define PCI_DEVICE_ID_AMD_17H_RR_NB    0x15d0
+#ifndef PCI_DEVICE_ID_AMD_17H_M10H_DF_F3
+#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F3       0x15eb
 #endif
 
 /* CPUID function 0x80000001, ebx */
@@ -63,10 +64,12 @@ static DEFINE_MUTEX(nb_smu_ind_mutex);
 #define  NB_CAP_HTC                    0x00000400
 
 /*
- * For F15h M60h, functionality of REG_REPORTED_TEMPERATURE
- * has been moved to D0F0xBC_xD820_0CA4 [Reported Temperature
- * Control]
+ * For F15h M60h and M70h, REG_HARDWARE_THERMAL_CONTROL
+ * and REG_REPORTED_TEMPERATURE have been moved to
+ * D0F0xBC_xD820_0C64 [Hardware Temperature Control]
+ * D0F0xBC_xD820_0CA4 [Reported Temperature Control]
  */
+#define F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET    0xd8200c64
 #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET    0xd8200ca4
 
 /* F17h M01h Access througn SMN */
@@ -74,6 +77,7 @@ static DEFINE_MUTEX(nb_smu_ind_mutex);
 
 struct k10temp_data {
        struct pci_dev *pdev;
+       void (*read_htcreg)(struct pci_dev *pdev, u32 *regval);
        void (*read_tempreg)(struct pci_dev *pdev, u32 *regval);
        int temp_offset;
        u32 temp_adjust_mask;
@@ -98,6 +102,11 @@ static const struct tctl_offset tctl_offset_table[] = {
        { 0x17, "AMD Ryzen Threadripper 1910", 10000 },
 };
 
+static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval)
+{
+       pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, regval);
+}
+
 static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval)
 {
        pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, regval);
@@ -114,6 +123,12 @@ static void amd_nb_index_read(struct pci_dev *pdev, unsigned int devfn,
        mutex_unlock(&nb_smu_ind_mutex);
 }
 
+static void read_htcreg_nb_f15(struct pci_dev *pdev, u32 *regval)
+{
+       amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
+                         F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET, regval);
+}
+
 static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval)
 {
        amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
@@ -122,8 +137,8 @@ static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval)
 
 static void read_tempreg_nb_f17(struct pci_dev *pdev, u32 *regval)
 {
-       amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0x60,
-                         F17H_M01H_REPORTED_TEMP_CTRL_OFFSET, regval);
+       amd_smn_read(amd_pci_dev_to_node_id(pdev),
+                    F17H_M01H_REPORTED_TEMP_CTRL_OFFSET, regval);
 }
 
 static ssize_t temp1_input_show(struct device *dev,
@@ -160,8 +175,7 @@ static ssize_t show_temp_crit(struct device *dev,
        u32 regval;
        int value;
 
-       pci_read_config_dword(data->pdev,
-                             REG_HARDWARE_THERMAL_CONTROL, &regval);
+       data->read_htcreg(data->pdev, &regval);
        value = ((regval >> 16) & 0x7f) * 500 + 52000;
        if (show_hyst)
                value -= ((regval >> 24) & 0xf) * 500;
@@ -181,13 +195,18 @@ static umode_t k10temp_is_visible(struct kobject *kobj,
        struct pci_dev *pdev = data->pdev;
 
        if (index >= 2) {
-               u32 reg_caps, reg_htc;
+               u32 reg;
+
+               if (!data->read_htcreg)
+                       return 0;
 
                pci_read_config_dword(pdev, REG_NORTHBRIDGE_CAPABILITIES,
-                                     &reg_caps);
-               pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL,
-                                     &reg_htc);
-               if (!(reg_caps & NB_CAP_HTC) || !(reg_htc & HTC_ENABLE))
+                                     &reg);
+               if (!(reg & NB_CAP_HTC))
+                       return 0;
+
+               data->read_htcreg(data->pdev, &reg);
+               if (!(reg & HTC_ENABLE))
                        return 0;
        }
        return attr->mode;
@@ -268,11 +287,13 @@ static int k10temp_probe(struct pci_dev *pdev,
 
        if (boot_cpu_data.x86 == 0x15 && (boot_cpu_data.x86_model == 0x60 ||
                                          boot_cpu_data.x86_model == 0x70)) {
+               data->read_htcreg = read_htcreg_nb_f15;
                data->read_tempreg = read_tempreg_nb_f15;
        } else if (boot_cpu_data.x86 == 0x17) {
                data->temp_adjust_mask = 0x80000;
                data->read_tempreg = read_tempreg_nb_f17;
        } else {
+               data->read_htcreg = read_htcreg_pci;
                data->read_tempreg = read_tempreg_pci;
        }
 
@@ -302,7 +323,7 @@ static const struct pci_device_id k10temp_id_table[] = {
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
-       { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_RR_NB) },
+       { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
        {}
 };
 MODULE_DEVICE_TABLE(pci, k10temp_id_table);
index fd36c39..0cdba29 100644 (file)
@@ -209,7 +209,10 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
        i2c_dw_disable_int(dev);
 
        /* Enable the adapter */
-       __i2c_dw_enable_and_wait(dev, true);
+       __i2c_dw_enable(dev, true);
+
+       /* Dummy read to avoid the register getting stuck on Bay Trail */
+       dw_readl(dev, DW_IC_ENABLE_STATUS);
 
        /* Clear and enable interrupts */
        dw_readl(dev, DW_IC_CLR_INTR);
index 2aa0e83..dae8ac6 100644 (file)
@@ -564,10 +564,10 @@ static int pmcmsptwi_master_xfer(struct i2c_adapter *adap,
                 * TODO: We could potentially loop and retry in the case
                 * of MSP_TWI_XFER_TIMEOUT.
                 */
-               return -1;
+               return -EIO;
        }
 
-       return 0;
+       return num;
 }
 
 static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter)
index e4be86b..7235c73 100644 (file)
@@ -337,7 +337,7 @@ static int vprbrd_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs,
                }
                mutex_unlock(&vb->lock);
        }
-       return 0;
+       return num;
 error:
        mutex_unlock(&vb->lock);
        return error;
index a9126b3..7c3b474 100644 (file)
@@ -445,10 +445,17 @@ static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
        msgs[1].buf = buffer;
 
        ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
-       if (ret < 0)
-               dev_err(&client->adapter->dev, "i2c read failed\n");
-       else
+       if (ret < 0) {
+               /* Getting a NACK is unfortunately normal with some DSTDs */
+               if (ret == -EREMOTEIO)
+                       dev_dbg(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n",
+                               data_len, client->addr, cmd, ret);
+               else
+                       dev_err(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n",
+                               data_len, client->addr, cmd, ret);
+       } else {
                memcpy(data, buffer, data_len);
+       }
 
        kfree(buffer);
        return ret;
index 77d257e..6d52ea0 100644 (file)
@@ -849,7 +849,7 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata,
        return 0;
 
 err_cqb:
-       kfree(*cqb);
+       kvfree(*cqb);
 
 err_db:
        mlx5_ib_db_unmap_user(to_mucontext(context), &cq->db);
index 4e63c6f..d030ce3 100644 (file)
@@ -250,7 +250,9 @@ void bch_debug_exit(void)
 
 int __init bch_debug_init(struct kobject *kobj)
 {
-       bcache_debug = debugfs_create_dir("bcache", NULL);
+       if (!IS_ENABLED(CONFIG_DEBUG_FS))
+               return 0;
 
+       bcache_debug = debugfs_create_dir("bcache", NULL);
        return IS_ERR_OR_NULL(bcache_debug);
 }
index a4c9c82..918d4fb 100644 (file)
@@ -717,6 +717,7 @@ struct cxl {
        bool perst_select_user;
        bool perst_same_image;
        bool psl_timebase_synced;
+       bool tunneled_ops_supported;
 
        /*
         * number of contexts mapped on to this card. Possible values are:
index 83f1d08..4d6736f 100644 (file)
@@ -1742,6 +1742,15 @@ static int cxl_configure_adapter(struct cxl *adapter, struct pci_dev *dev)
        /* Required for devices using CAPP DMA mode, harmless for others */
        pci_set_master(dev);
 
+       adapter->tunneled_ops_supported = false;
+
+       if (cxl_is_power9()) {
+               if (pnv_pci_set_tunnel_bar(dev, 0x00020000E0000000ull, 1))
+                       dev_info(&dev->dev, "Tunneled operations unsupported\n");
+               else
+                       adapter->tunneled_ops_supported = true;
+       }
+
        if ((rc = pnv_phb_to_cxl_mode(dev, adapter->native->sl_ops->capi_mode)))
                goto err;
 
@@ -1768,6 +1777,9 @@ static void cxl_deconfigure_adapter(struct cxl *adapter)
 {
        struct pci_dev *pdev = to_pci_dev(adapter->dev.parent);
 
+       if (cxl_is_power9())
+               pnv_pci_set_tunnel_bar(pdev, 0x00020000E0000000ull, 0);
+
        cxl_native_release_psl_err_irq(adapter);
        cxl_unmap_adapter_regs(adapter);
 
index 95285b7..4b5a4c5 100644 (file)
@@ -78,6 +78,15 @@ static ssize_t psl_timebase_synced_show(struct device *device,
        return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_timebase_synced);
 }
 
+static ssize_t tunneled_ops_supported_show(struct device *device,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct cxl *adapter = to_cxl_adapter(device);
+
+       return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->tunneled_ops_supported);
+}
+
 static ssize_t reset_adapter_store(struct device *device,
                                   struct device_attribute *attr,
                                   const char *buf, size_t count)
@@ -183,6 +192,7 @@ static struct device_attribute adapter_attrs[] = {
        __ATTR_RO(base_image),
        __ATTR_RO(image_loaded),
        __ATTR_RO(psl_timebase_synced),
+       __ATTR_RO(tunneled_ops_supported),
        __ATTR_RW(load_image_on_perst),
        __ATTR_RW(perst_reloads_same_image),
        __ATTR(reset, S_IWUSR, NULL, reset_adapter_store),
index 0c125f2..33053b0 100644 (file)
@@ -518,7 +518,7 @@ static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata)
        if (of_node && of_match_device(at24_of_match, dev))
                cdata = of_device_get_match_data(dev);
        else if (id)
-               cdata = (void *)&id->driver_data;
+               cdata = (void *)id->driver_data;
        else
                cdata = acpi_device_get_match_data(dev);
 
index db5ec4e..ebb1d14 100644 (file)
@@ -1194,11 +1194,13 @@ static void marvell_nfc_hw_ecc_bch_read_chunk(struct nand_chip *chip, int chunk,
                                  NDCB0_CMD2(NAND_CMD_READSTART);
 
        /*
-        * Trigger the naked read operation only on the last chunk.
-        * Otherwise, use monolithic read.
+        * Trigger the monolithic read on the first chunk, then naked read on
+        * intermediate chunks and finally a last naked read on the last chunk.
         */
-       if (lt->nchunks == 1 || (chunk < lt->nchunks - 1))
+       if (chunk == 0)
                nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW);
+       else if (chunk < lt->nchunks - 1)
+               nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_NAKED_RW);
        else
                nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW);
 
index a8f6098..e82108c 100644 (file)
 #include <net/bonding.h>
 #include <net/bond_alb.h>
 
-
-
-static const u8 mac_bcast[ETH_ALEN + 2] __long_aligned = {
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-};
 static const u8 mac_v6_allmcast[ETH_ALEN + 2] __long_aligned = {
        0x33, 0x33, 0x00, 0x00, 0x00, 0x01
 };
@@ -420,8 +415,7 @@ static void rlb_clear_slave(struct bonding *bond, struct slave *slave)
 
                        if (assigned_slave) {
                                rx_hash_table[index].slave = assigned_slave;
-                               if (!ether_addr_equal_64bits(rx_hash_table[index].mac_dst,
-                                                            mac_bcast)) {
+                               if (is_valid_ether_addr(rx_hash_table[index].mac_dst)) {
                                        bond_info->rx_hashtbl[index].ntt = 1;
                                        bond_info->rx_ntt = 1;
                                        /* A slave has been removed from the
@@ -524,7 +518,7 @@ static void rlb_req_update_slave_clients(struct bonding *bond, struct slave *sla
                client_info = &(bond_info->rx_hashtbl[hash_index]);
 
                if ((client_info->slave == slave) &&
-                   !ether_addr_equal_64bits(client_info->mac_dst, mac_bcast)) {
+                   is_valid_ether_addr(client_info->mac_dst)) {
                        client_info->ntt = 1;
                        ntt = 1;
                }
@@ -565,7 +559,7 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, __be32 src_ip)
                if ((client_info->ip_src == src_ip) &&
                    !ether_addr_equal_64bits(client_info->slave->dev->dev_addr,
                                             bond->dev->dev_addr) &&
-                   !ether_addr_equal_64bits(client_info->mac_dst, mac_bcast)) {
+                   is_valid_ether_addr(client_info->mac_dst)) {
                        client_info->ntt = 1;
                        bond_info->rx_ntt = 1;
                }
@@ -593,7 +587,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
                if ((client_info->ip_src == arp->ip_src) &&
                    (client_info->ip_dst == arp->ip_dst)) {
                        /* the entry is already assigned to this client */
-                       if (!ether_addr_equal_64bits(arp->mac_dst, mac_bcast)) {
+                       if (!is_broadcast_ether_addr(arp->mac_dst)) {
                                /* update mac address from arp */
                                ether_addr_copy(client_info->mac_dst, arp->mac_dst);
                        }
@@ -641,7 +635,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
                ether_addr_copy(client_info->mac_src, arp->mac_src);
                client_info->slave = assigned_slave;
 
-               if (!ether_addr_equal_64bits(client_info->mac_dst, mac_bcast)) {
+               if (is_valid_ether_addr(client_info->mac_dst)) {
                        client_info->ntt = 1;
                        bond->alb_info.rx_ntt = 1;
                } else {
@@ -733,8 +727,10 @@ static void rlb_rebalance(struct bonding *bond)
                assigned_slave = __rlb_next_rx_slave(bond);
                if (assigned_slave && (client_info->slave != assigned_slave)) {
                        client_info->slave = assigned_slave;
-                       client_info->ntt = 1;
-                       ntt = 1;
+                       if (!is_zero_ether_addr(client_info->mac_dst)) {
+                               client_info->ntt = 1;
+                               ntt = 1;
+                       }
                }
        }
 
@@ -1412,9 +1408,9 @@ netdev_tx_t bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        case ETH_P_IP: {
                const struct iphdr *iph = ip_hdr(skb);
 
-               if (ether_addr_equal_64bits(eth_data->h_dest, mac_bcast) ||
-                   (iph->daddr == ip_bcast) ||
-                   (iph->protocol == IPPROTO_IGMP)) {
+               if (is_broadcast_ether_addr(eth_data->h_dest) ||
+                   iph->daddr == ip_bcast ||
+                   iph->protocol == IPPROTO_IGMP) {
                        do_tx_balance = false;
                        break;
                }
@@ -1426,7 +1422,7 @@ netdev_tx_t bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                /* IPv6 doesn't really use broadcast mac address, but leave
                 * that here just in case.
                 */
-               if (ether_addr_equal_64bits(eth_data->h_dest, mac_bcast)) {
+               if (is_broadcast_ether_addr(eth_data->h_dest)) {
                        do_tx_balance = false;
                        break;
                }
@@ -1482,8 +1478,24 @@ netdev_tx_t bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        }
 
        if (do_tx_balance) {
-               hash_index = _simple_hash(hash_start, hash_size);
-               tx_slave = tlb_choose_channel(bond, hash_index, skb->len);
+               if (bond->params.tlb_dynamic_lb) {
+                       hash_index = _simple_hash(hash_start, hash_size);
+                       tx_slave = tlb_choose_channel(bond, hash_index, skb->len);
+               } else {
+                       /*
+                        * do_tx_balance means we are free to select the tx_slave
+                        * So we do exactly what tlb would do for hash selection
+                        */
+
+                       struct bond_up_slave *slaves;
+                       unsigned int count;
+
+                       slaves = rcu_dereference(bond->slave_arr);
+                       count = slaves ? READ_ONCE(slaves->count) : 0;
+                       if (likely(count))
+                               tx_slave = slaves->arr[bond_xmit_hash(bond, skb) %
+                                                      count];
+               }
        }
 
        return bond_do_alb_xmit(skb, bond, tx_slave);
index 4176e1d..fea17b9 100644 (file)
@@ -159,7 +159,7 @@ module_param(min_links, int, 0);
 MODULE_PARM_DESC(min_links, "Minimum number of available links before turning on carrier");
 
 module_param(xmit_hash_policy, charp, 0);
-MODULE_PARM_DESC(xmit_hash_policy, "balance-xor and 802.3ad hashing method; "
+MODULE_PARM_DESC(xmit_hash_policy, "balance-alb, balance-tlb, balance-xor, 802.3ad hashing method; "
                                   "0 for layer 2 (default), 1 for layer 3+4, "
                                   "2 for layer 2+3, 3 for encap layer 2+3, "
                                   "4 for encap layer 3+4");
@@ -1107,7 +1107,8 @@ static void bond_compute_features(struct bonding *bond)
 
 done:
        bond_dev->vlan_features = vlan_features;
-       bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL;
+       bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
+                                   NETIF_F_GSO_UDP_L4;
        bond_dev->gso_max_segs = gso_max_segs;
        netif_set_gso_max_size(bond_dev, gso_max_size);
 
@@ -1735,7 +1736,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
                unblock_netpoll_tx();
        }
 
-       if (bond_mode_uses_xmit_hash(bond))
+       if (bond_mode_can_use_xmit_hash(bond))
                bond_update_slave_arr(bond, NULL);
 
        bond->nest_level = dev_get_nest_level(bond_dev);
@@ -1870,7 +1871,7 @@ static int __bond_release_one(struct net_device *bond_dev,
        if (BOND_MODE(bond) == BOND_MODE_8023AD)
                bond_3ad_unbind_slave(slave);
 
-       if (bond_mode_uses_xmit_hash(bond))
+       if (bond_mode_can_use_xmit_hash(bond))
                bond_update_slave_arr(bond, slave);
 
        netdev_info(bond_dev, "Releasing %s interface %s\n",
@@ -2137,6 +2138,24 @@ static int bond_miimon_inspect(struct bonding *bond)
        return commit;
 }
 
+static void bond_miimon_link_change(struct bonding *bond,
+                                   struct slave *slave,
+                                   char link)
+{
+       switch (BOND_MODE(bond)) {
+       case BOND_MODE_8023AD:
+               bond_3ad_handle_link_change(slave, link);
+               break;
+       case BOND_MODE_TLB:
+       case BOND_MODE_ALB:
+               bond_alb_handle_link_change(bond, slave, link);
+               break;
+       case BOND_MODE_XOR:
+               bond_update_slave_arr(bond, NULL);
+               break;
+       }
+}
+
 static void bond_miimon_commit(struct bonding *bond)
 {
        struct list_head *iter;
@@ -2178,16 +2197,7 @@ static void bond_miimon_commit(struct bonding *bond)
                                    slave->speed == SPEED_UNKNOWN ? 0 : slave->speed,
                                    slave->duplex ? "full" : "half");
 
-                       /* notify ad that the link status has changed */
-                       if (BOND_MODE(bond) == BOND_MODE_8023AD)
-                               bond_3ad_handle_link_change(slave, BOND_LINK_UP);
-
-                       if (bond_is_lb(bond))
-                               bond_alb_handle_link_change(bond, slave,
-                                                           BOND_LINK_UP);
-
-                       if (BOND_MODE(bond) == BOND_MODE_XOR)
-                               bond_update_slave_arr(bond, NULL);
+                       bond_miimon_link_change(bond, slave, BOND_LINK_UP);
 
                        if (!bond->curr_active_slave || slave == primary)
                                goto do_failover;
@@ -2209,16 +2219,7 @@ static void bond_miimon_commit(struct bonding *bond)
                        netdev_info(bond->dev, "link status definitely down for interface %s, disabling it\n",
                                    slave->dev->name);
 
-                       if (BOND_MODE(bond) == BOND_MODE_8023AD)
-                               bond_3ad_handle_link_change(slave,
-                                                           BOND_LINK_DOWN);
-
-                       if (bond_is_lb(bond))
-                               bond_alb_handle_link_change(bond, slave,
-                                                           BOND_LINK_DOWN);
-
-                       if (BOND_MODE(bond) == BOND_MODE_XOR)
-                               bond_update_slave_arr(bond, NULL);
+                       bond_miimon_link_change(bond, slave, BOND_LINK_DOWN);
 
                        if (slave == rcu_access_pointer(bond->curr_active_slave))
                                goto do_failover;
@@ -3102,7 +3103,7 @@ static int bond_slave_netdev_event(unsigned long event,
                 * events. If these (miimon/arpmon) parameters are configured
                 * then array gets refreshed twice and that should be fine!
                 */
-               if (bond_mode_uses_xmit_hash(bond))
+               if (bond_mode_can_use_xmit_hash(bond))
                        bond_update_slave_arr(bond, NULL);
                break;
        case NETDEV_CHANGEMTU:
@@ -3322,7 +3323,7 @@ static int bond_open(struct net_device *bond_dev)
                 */
                if (bond_alb_initialize(bond, (BOND_MODE(bond) == BOND_MODE_ALB)))
                        return -ENOMEM;
-               if (bond->params.tlb_dynamic_lb)
+               if (bond->params.tlb_dynamic_lb || BOND_MODE(bond) == BOND_MODE_ALB)
                        queue_delayed_work(bond->wq, &bond->alb_work, 0);
        }
 
@@ -3341,7 +3342,7 @@ static int bond_open(struct net_device *bond_dev)
                bond_3ad_initiate_agg_selection(bond, 1);
        }
 
-       if (bond_mode_uses_xmit_hash(bond))
+       if (bond_mode_can_use_xmit_hash(bond))
                bond_update_slave_arr(bond, NULL);
 
        return 0;
@@ -3894,7 +3895,7 @@ err:
  * to determine the slave interface -
  * (a) BOND_MODE_8023AD
  * (b) BOND_MODE_XOR
- * (c) BOND_MODE_TLB && tlb_dynamic_lb == 0
+ * (c) (BOND_MODE_TLB || BOND_MODE_ALB) && tlb_dynamic_lb == 0
  *
  * The caller is expected to hold RTNL only and NO other lock!
  */
@@ -3947,6 +3948,11 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave)
                        continue;
                if (skipslave == slave)
                        continue;
+
+               netdev_dbg(bond->dev,
+                          "Adding slave dev %s to tx hash array[%d]\n",
+                          slave->dev->name, new_arr->count);
+
                new_arr->arr[new_arr->count++] = slave;
        }
 
@@ -4263,7 +4269,7 @@ void bond_setup(struct net_device *bond_dev)
                                NETIF_F_HW_VLAN_CTAG_RX |
                                NETIF_F_HW_VLAN_CTAG_FILTER;
 
-       bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
+       bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL | NETIF_F_GSO_UDP_L4;
        bond_dev->features |= bond_dev->hw_features;
 }
 
@@ -4324,9 +4330,9 @@ static int bond_check_params(struct bond_params *params)
        }
 
        if (xmit_hash_policy) {
-               if ((bond_mode != BOND_MODE_XOR) &&
-                   (bond_mode != BOND_MODE_8023AD) &&
-                   (bond_mode != BOND_MODE_TLB)) {
+               if (bond_mode == BOND_MODE_ROUNDROBIN ||
+                   bond_mode == BOND_MODE_ACTIVEBACKUP ||
+                   bond_mode == BOND_MODE_BROADCAST) {
                        pr_info("xmit_hash_policy param is irrelevant in mode %s\n",
                                bond_mode_name(bond_mode));
                } else {
index 58c705f..8a945c9 100644 (file)
@@ -395,7 +395,7 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = {
                .id = BOND_OPT_TLB_DYNAMIC_LB,
                .name = "tlb_dynamic_lb",
                .desc = "Enable dynamic flow shuffling",
-               .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_TLB)),
+               .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_TLB) | BIT(BOND_MODE_ALB)),
                .values = bond_tlb_dynamic_lb_tbl,
                .flags = BOND_OPTFLAG_IFDOWN,
                .set = bond_option_tlb_dynamic_lb_set,
index ac621f4..02e8982 100644 (file)
@@ -450,12 +450,8 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds)
        priv->slave_mii_bus->parent = ds->dev->parent;
        priv->slave_mii_bus->phy_mask = ~priv->indir_phy_mask;
 
-       if (dn)
-               err = of_mdiobus_register(priv->slave_mii_bus, dn);
-       else
-               err = mdiobus_register(priv->slave_mii_bus);
-
-       if (err)
+       err = of_mdiobus_register(priv->slave_mii_bus, dn);
+       if (err && dn)
                of_node_put(dn);
 
        return err;
index 23b45da..b89acae 100644 (file)
@@ -354,10 +354,13 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
        /* Locate the first rule available */
        if (fs->location == RX_CLS_LOC_ANY)
                rule_index = find_first_zero_bit(priv->cfp.used,
-                                                bcm_sf2_cfp_rule_size(priv));
+                                                priv->num_cfp_rules);
        else
                rule_index = fs->location;
 
+       if (rule_index > bcm_sf2_cfp_rule_size(priv))
+               return -ENOSPC;
+
        layout = &udf_tcpip4_layout;
        /* We only use one UDF slice for now */
        slice_num = bcm_sf2_get_slice_number(layout, 0);
@@ -562,19 +565,21 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
         * first half because the HW search is by incrementing addresses.
         */
        if (fs->location == RX_CLS_LOC_ANY)
-               rule_index[0] = find_first_zero_bit(priv->cfp.used,
-                                                   bcm_sf2_cfp_rule_size(priv));
+               rule_index[1] = find_first_zero_bit(priv->cfp.used,
+                                                   priv->num_cfp_rules);
        else
-               rule_index[0] = fs->location;
+               rule_index[1] = fs->location;
+       if (rule_index[1] > bcm_sf2_cfp_rule_size(priv))
+               return -ENOSPC;
 
        /* Flag it as used (cleared on error path) such that we can immediately
         * obtain a second one to chain from.
         */
-       set_bit(rule_index[0], priv->cfp.used);
+       set_bit(rule_index[1], priv->cfp.used);
 
-       rule_index[1] = find_first_zero_bit(priv->cfp.used,
-                                           bcm_sf2_cfp_rule_size(priv));
-       if (rule_index[1] > bcm_sf2_cfp_rule_size(priv)) {
+       rule_index[0] = find_first_zero_bit(priv->cfp.used,
+                                           priv->num_cfp_rules);
+       if (rule_index[0] > bcm_sf2_cfp_rule_size(priv)) {
                ret = -ENOSPC;
                goto out_err;
        }
@@ -712,14 +717,14 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
        /* Flag the second half rule as being used now, return it as the
         * location, and flag it as unique while dumping rules
         */
-       set_bit(rule_index[1], priv->cfp.used);
+       set_bit(rule_index[0], priv->cfp.used);
        set_bit(rule_index[1], priv->cfp.unique);
        fs->location = rule_index[1];
 
        return ret;
 
 out_err:
-       clear_bit(rule_index[0], priv->cfp.used);
+       clear_bit(rule_index[1], priv->cfp.used);
        return ret;
 }
 
@@ -785,10 +790,6 @@ static int bcm_sf2_cfp_rule_del_one(struct bcm_sf2_priv *priv, int port,
        int ret;
        u32 reg;
 
-       /* Refuse deletion of unused rules, and the default reserved rule */
-       if (!test_bit(loc, priv->cfp.used) || loc == 0)
-               return -EINVAL;
-
        /* Indicate which rule we want to read */
        bcm_sf2_cfp_rule_addr_set(priv, loc);
 
@@ -826,6 +827,13 @@ static int bcm_sf2_cfp_rule_del(struct bcm_sf2_priv *priv, int port,
        u32 next_loc = 0;
        int ret;
 
+       /* Refuse deleting unused rules, and those that are not unique since
+        * that could leave IPv6 rules with one of the chained rule in the
+        * table.
+        */
+       if (!test_bit(loc, priv->cfp.unique) || loc == 0)
+               return -EINVAL;
+
        ret = bcm_sf2_cfp_rule_del_one(priv, port, loc, &next_loc);
        if (ret)
                return ret;
index 2910f68..12df00f 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/of_device.h>
 #include <linux/of_irq.h>
 #include <linux/of_mdio.h>
+#include <linux/platform_data/mv88e6xxx.h>
 #include <linux/netdevice.h>
 #include <linux/gpio/consumer.h>
 #include <linux/phy.h>
@@ -995,14 +996,6 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
 
 }
 
-static int mv88e6xxx_stats_set_histogram(struct mv88e6xxx_chip *chip)
-{
-       if (chip->info->ops->stats_set_histogram)
-               return chip->info->ops->stats_set_histogram(chip);
-
-       return 0;
-}
-
 static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
 {
        return 32 * sizeof(u16);
@@ -1104,6 +1097,25 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
                dev_err(ds->dev, "p%d: failed to update state\n", port);
 }
 
+static int mv88e6xxx_pri_setup(struct mv88e6xxx_chip *chip)
+{
+       int err;
+
+       if (chip->info->ops->ieee_pri_map) {
+               err = chip->info->ops->ieee_pri_map(chip);
+               if (err)
+                       return err;
+       }
+
+       if (chip->info->ops->ip_pri_map) {
+               err = chip->info->ops->ip_pri_map(chip);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int mv88e6xxx_devmap_setup(struct mv88e6xxx_chip *chip)
 {
        int target, port;
@@ -2248,45 +2260,16 @@ static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
        return err;
 }
 
-static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
+static int mv88e6xxx_stats_setup(struct mv88e6xxx_chip *chip)
 {
        int err;
 
-       /* Configure the IP ToS mapping registers. */
-       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_0, 0x0000);
-       if (err)
-               return err;
-       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_1, 0x0000);
-       if (err)
-               return err;
-       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_2, 0x5555);
-       if (err)
-               return err;
-       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_3, 0x5555);
-       if (err)
-               return err;
-       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_4, 0xaaaa);
-       if (err)
-               return err;
-       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_5, 0xaaaa);
-       if (err)
-               return err;
-       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_6, 0xffff);
-       if (err)
-               return err;
-       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_7, 0xffff);
-       if (err)
-               return err;
-
-       /* Configure the IEEE 802.1p priority mapping register. */
-       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IEEE_PRI, 0xfa41);
-       if (err)
-               return err;
-
        /* Initialize the statistics unit */
-       err = mv88e6xxx_stats_set_histogram(chip);
-       if (err)
-               return err;
+       if (chip->info->ops->stats_set_histogram) {
+               err = chip->info->ops->stats_set_histogram(chip);
+               if (err)
+                       return err;
+       }
 
        return mv88e6xxx_g1_stats_clear(chip);
 }
@@ -2312,11 +2295,6 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
                        goto unlock;
        }
 
-       /* Setup Switch Global 1 Registers */
-       err = mv88e6xxx_g1_setup(chip);
-       if (err)
-               goto unlock;
-
        err = mv88e6xxx_irl_setup(chip);
        if (err)
                goto unlock;
@@ -2365,6 +2343,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
        if (err)
                goto unlock;
 
+       err = mv88e6xxx_pri_setup(chip);
+       if (err)
+               goto unlock;
+
        /* Setup PTP Hardware Clock and timestamping */
        if (chip->info->ptp_support) {
                err = mv88e6xxx_ptp_setup(chip);
@@ -2376,6 +2358,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
                        goto unlock;
        }
 
+       err = mv88e6xxx_stats_setup(chip);
+       if (err)
+               goto unlock;
+
 unlock:
        mutex_unlock(&chip->reg_lock);
 
@@ -2469,10 +2455,7 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
                        return err;
        }
 
-       if (np)
-               err = of_mdiobus_register(bus, np);
-       else
-               err = mdiobus_register(bus);
+       err = of_mdiobus_register(bus, np);
        if (err) {
                dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
                mv88e6xxx_g2_irq_mdio_free(chip, bus);
@@ -2592,6 +2575,8 @@ static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
 
 static const struct mv88e6xxx_ops mv88e6085_ops = {
        /* MV88E6XXX_FAMILY_6097 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
        .phy_read = mv88e6185_phy_ppu_read,
@@ -2628,6 +2613,8 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {
 
 static const struct mv88e6xxx_ops mv88e6095_ops = {
        /* MV88E6XXX_FAMILY_6095 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
        .phy_read = mv88e6185_phy_ppu_read,
        .phy_write = mv88e6185_phy_ppu_write,
@@ -2652,6 +2639,8 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {
 
 static const struct mv88e6xxx_ops mv88e6097_ops = {
        /* MV88E6XXX_FAMILY_6097 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
        .phy_read = mv88e6xxx_g2_smi_phy_read,
@@ -2686,6 +2675,8 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
 
 static const struct mv88e6xxx_ops mv88e6123_ops = {
        /* MV88E6XXX_FAMILY_6165 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
        .phy_read = mv88e6xxx_g2_smi_phy_read,
@@ -2714,6 +2705,8 @@ static const struct mv88e6xxx_ops mv88e6123_ops = {
 
 static const struct mv88e6xxx_ops mv88e6131_ops = {
        /* MV88E6XXX_FAMILY_6185 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
        .phy_read = mv88e6185_phy_ppu_read,
        .phy_write = mv88e6185_phy_ppu_write,
@@ -2747,6 +2740,8 @@ static const struct mv88e6xxx_ops mv88e6131_ops = {
 
 static const struct mv88e6xxx_ops mv88e6141_ops = {
        /* MV88E6XXX_FAMILY_6341 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .get_eeprom = mv88e6xxx_g2_get_eeprom8,
        .set_eeprom = mv88e6xxx_g2_set_eeprom8,
@@ -2784,6 +2779,8 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
 
 static const struct mv88e6xxx_ops mv88e6161_ops = {
        /* MV88E6XXX_FAMILY_6165 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
        .phy_read = mv88e6xxx_g2_smi_phy_read,
@@ -2817,6 +2814,8 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {
 
 static const struct mv88e6xxx_ops mv88e6165_ops = {
        /* MV88E6XXX_FAMILY_6165 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
        .phy_read = mv88e6165_phy_read,
@@ -2843,6 +2842,8 @@ static const struct mv88e6xxx_ops mv88e6165_ops = {
 
 static const struct mv88e6xxx_ops mv88e6171_ops = {
        /* MV88E6XXX_FAMILY_6351 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
        .phy_read = mv88e6xxx_g2_smi_phy_read,
@@ -2877,6 +2878,8 @@ static const struct mv88e6xxx_ops mv88e6171_ops = {
 
 static const struct mv88e6xxx_ops mv88e6172_ops = {
        /* MV88E6XXX_FAMILY_6352 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .get_eeprom = mv88e6xxx_g2_get_eeprom16,
        .set_eeprom = mv88e6xxx_g2_set_eeprom16,
@@ -2916,6 +2919,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
 
 static const struct mv88e6xxx_ops mv88e6175_ops = {
        /* MV88E6XXX_FAMILY_6351 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
        .phy_read = mv88e6xxx_g2_smi_phy_read,
@@ -2951,6 +2956,8 @@ static const struct mv88e6xxx_ops mv88e6175_ops = {
 
 static const struct mv88e6xxx_ops mv88e6176_ops = {
        /* MV88E6XXX_FAMILY_6352 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .get_eeprom = mv88e6xxx_g2_get_eeprom16,
        .set_eeprom = mv88e6xxx_g2_set_eeprom16,
@@ -2990,6 +2997,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
 
 static const struct mv88e6xxx_ops mv88e6185_ops = {
        /* MV88E6XXX_FAMILY_6185 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
        .phy_read = mv88e6185_phy_ppu_read,
        .phy_write = mv88e6185_phy_ppu_write,
@@ -3129,6 +3138,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
 
 static const struct mv88e6xxx_ops mv88e6240_ops = {
        /* MV88E6XXX_FAMILY_6352 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .get_eeprom = mv88e6xxx_g2_get_eeprom16,
        .set_eeprom = mv88e6xxx_g2_set_eeprom16,
@@ -3208,6 +3219,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
 
 static const struct mv88e6xxx_ops mv88e6320_ops = {
        /* MV88E6XXX_FAMILY_6320 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .get_eeprom = mv88e6xxx_g2_get_eeprom16,
        .set_eeprom = mv88e6xxx_g2_set_eeprom16,
@@ -3244,6 +3257,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
 
 static const struct mv88e6xxx_ops mv88e6321_ops = {
        /* MV88E6XXX_FAMILY_6320 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .get_eeprom = mv88e6xxx_g2_get_eeprom16,
        .set_eeprom = mv88e6xxx_g2_set_eeprom16,
@@ -3278,6 +3293,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
 
 static const struct mv88e6xxx_ops mv88e6341_ops = {
        /* MV88E6XXX_FAMILY_6341 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .get_eeprom = mv88e6xxx_g2_get_eeprom8,
        .set_eeprom = mv88e6xxx_g2_set_eeprom8,
@@ -3316,6 +3333,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
 
 static const struct mv88e6xxx_ops mv88e6350_ops = {
        /* MV88E6XXX_FAMILY_6351 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
        .phy_read = mv88e6xxx_g2_smi_phy_read,
@@ -3350,6 +3369,8 @@ static const struct mv88e6xxx_ops mv88e6350_ops = {
 
 static const struct mv88e6xxx_ops mv88e6351_ops = {
        /* MV88E6XXX_FAMILY_6351 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
        .phy_read = mv88e6xxx_g2_smi_phy_read,
@@ -3385,6 +3406,8 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
 
 static const struct mv88e6xxx_ops mv88e6352_ops = {
        /* MV88E6XXX_FAMILY_6352 */
+       .ieee_pri_map = mv88e6085_g1_ieee_pri_map,
+       .ip_pri_map = mv88e6085_g1_ip_pri_map,
        .irl_init_all = mv88e6352_g2_irl_init_all,
        .get_eeprom = mv88e6xxx_g2_get_eeprom16,
        .set_eeprom = mv88e6xxx_g2_set_eeprom16,
@@ -4328,6 +4351,7 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
                return -ENOMEM;
 
        ds->priv = chip;
+       ds->dev = dev;
        ds->ops = &mv88e6xxx_switch_ops;
        ds->ageing_time_min = chip->info->age_time_coeff;
        ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
@@ -4342,42 +4366,82 @@ static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
        dsa_unregister_switch(chip->ds);
 }
 
+static const void *pdata_device_get_match_data(struct device *dev)
+{
+       const struct of_device_id *matches = dev->driver->of_match_table;
+       const struct dsa_mv88e6xxx_pdata *pdata = dev->platform_data;
+
+       for (; matches->name[0] || matches->type[0] || matches->compatible[0];
+            matches++) {
+               if (!strcmp(pdata->compatible, matches->compatible))
+                       return matches->data;
+       }
+       return NULL;
+}
+
 static int mv88e6xxx_probe(struct mdio_device *mdiodev)
 {
+       struct dsa_mv88e6xxx_pdata *pdata = mdiodev->dev.platform_data;
+       const struct mv88e6xxx_info *compat_info = NULL;
        struct device *dev = &mdiodev->dev;
        struct device_node *np = dev->of_node;
-       const struct mv88e6xxx_info *compat_info;
        struct mv88e6xxx_chip *chip;
-       u32 eeprom_len;
+       int port;
        int err;
 
-       compat_info = of_device_get_match_data(dev);
+       if (np)
+               compat_info = of_device_get_match_data(dev);
+
+       if (pdata) {
+               compat_info = pdata_device_get_match_data(dev);
+
+               if (!pdata->netdev)
+                       return -EINVAL;
+
+               for (port = 0; port < DSA_MAX_PORTS; port++) {
+                       if (!(pdata->enabled_ports & (1 << port)))
+                               continue;
+                       if (strcmp(pdata->cd.port_names[port], "cpu"))
+                               continue;
+                       pdata->cd.netdev[port] = &pdata->netdev->dev;
+                       break;
+               }
+       }
+
        if (!compat_info)
                return -EINVAL;
 
        chip = mv88e6xxx_alloc_chip(dev);
-       if (!chip)
-               return -ENOMEM;
+       if (!chip) {
+               err = -ENOMEM;
+               goto out;
+       }
 
        chip->info = compat_info;
 
        err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
        if (err)
-               return err;
+               goto out;
 
        chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
-       if (IS_ERR(chip->reset))
-               return PTR_ERR(chip->reset);
+       if (IS_ERR(chip->reset)) {
+               err = PTR_ERR(chip->reset);
+               goto out;
+       }
 
        err = mv88e6xxx_detect(chip);
        if (err)
-               return err;
+               goto out;
 
        mv88e6xxx_phy_init(chip);
 
-       if (chip->info->ops->get_eeprom &&
-           !of_property_read_u32(np, "eeprom-length", &eeprom_len))
-               chip->eeprom_len = eeprom_len;
+       if (chip->info->ops->get_eeprom) {
+               if (np)
+                       of_property_read_u32(np, "eeprom-length",
+                                            &chip->eeprom_len);
+               else
+                       chip->eeprom_len = pdata->eeprom_len;
+       }
 
        mutex_lock(&chip->reg_lock);
        err = mv88e6xxx_switch_reset(chip);
@@ -4446,6 +4510,9 @@ out_g1_irq:
                mv88e6xxx_irq_poll_free(chip);
        mutex_unlock(&chip->reg_lock);
 out:
+       if (pdata)
+               dev_put(pdata->netdev);
+
        return err;
 }
 
index 8eb5dfe..8ac3fbb 100644 (file)
@@ -238,7 +238,7 @@ struct mv88e6xxx_chip {
        struct gpio_desc *reset;
 
        /* set to size of eeprom if supported by the switch */
-       int             eeprom_len;
+       u32 eeprom_len;
 
        /* List of mdio busses */
        struct list_head mdios;
@@ -294,6 +294,9 @@ struct mv88e6xxx_mdio_bus {
 };
 
 struct mv88e6xxx_ops {
+       int (*ieee_pri_map)(struct mv88e6xxx_chip *chip);
+       int (*ip_pri_map)(struct mv88e6xxx_chip *chip);
+
        /* Ingress Rate Limit unit (IRL) operations */
        int (*irl_init_all)(struct mv88e6xxx_chip *chip, int port);
 
index 244ee1f..d721ccf 100644 (file)
@@ -241,6 +241,64 @@ int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip)
        return mv88e6185_g1_wait_ppu_disabled(chip);
 }
 
+/* Offset 0x10: IP-PRI Mapping Register 0
+ * Offset 0x11: IP-PRI Mapping Register 1
+ * Offset 0x12: IP-PRI Mapping Register 2
+ * Offset 0x13: IP-PRI Mapping Register 3
+ * Offset 0x14: IP-PRI Mapping Register 4
+ * Offset 0x15: IP-PRI Mapping Register 5
+ * Offset 0x16: IP-PRI Mapping Register 6
+ * Offset 0x17: IP-PRI Mapping Register 7
+ */
+
+int mv88e6085_g1_ip_pri_map(struct mv88e6xxx_chip *chip)
+{
+       int err;
+
+       /* Reset the IP TOS/DiffServ/Traffic priorities to defaults */
+       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_0, 0x0000);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_1, 0x0000);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_2, 0x5555);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_3, 0x5555);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_4, 0xaaaa);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_5, 0xaaaa);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_6, 0xffff);
+       if (err)
+               return err;
+
+       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IP_PRI_7, 0xffff);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+/* Offset 0x18: IEEE-PRI Register */
+
+int mv88e6085_g1_ieee_pri_map(struct mv88e6xxx_chip *chip)
+{
+       /* Reset the IEEE Tag priorities to defaults */
+       return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IEEE_PRI, 0xfa41);
+}
+
 /* Offset 0x1a: Monitor Control */
 /* Offset 0x1a: Monitor & MGMT Control on some devices */
 
@@ -393,18 +451,9 @@ int mv88e6390_g1_rmu_disable(struct mv88e6xxx_chip *chip)
 
 int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip)
 {
-       u16 val;
-       int err;
-
-       err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL2, &val);
-       if (err)
-               return err;
-
-       val |= MV88E6XXX_G1_CTL2_HIST_RX_TX;
-
-       err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL2, val);
-
-       return err;
+       return mv88e6xxx_g1_ctl2_mask(chip, MV88E6390_G1_CTL2_HIST_MODE_MASK,
+                                     MV88E6390_G1_CTL2_HIST_MODE_RX |
+                                     MV88E6390_G1_CTL2_HIST_MODE_TX);
 }
 
 int mv88e6xxx_g1_set_device_number(struct mv88e6xxx_chip *chip, int index)
index e186a02..7c791c1 100644 (file)
 
 /* Offset 0x1C: Global Control 2 */
 #define MV88E6XXX_G1_CTL2                      0x1c
-#define MV88E6XXX_G1_CTL2_HIST_RX              0x0040
-#define MV88E6XXX_G1_CTL2_HIST_TX              0x0080
-#define MV88E6XXX_G1_CTL2_HIST_RX_TX           0x00c0
 #define MV88E6185_G1_CTL2_CASCADE_PORT_MASK    0xf000
 #define MV88E6185_G1_CTL2_CASCADE_PORT_NONE    0xe000
 #define MV88E6185_G1_CTL2_CASCADE_PORT_MULTI   0xf000
+#define MV88E6352_G1_CTL2_HEADER_TYPE_MASK     0xc000
+#define MV88E6352_G1_CTL2_HEADER_TYPE_ORIG     0x0000
+#define MV88E6352_G1_CTL2_HEADER_TYPE_MGMT     0x4000
+#define MV88E6390_G1_CTL2_HEADER_TYPE_LAG      0x8000
 #define MV88E6352_G1_CTL2_RMU_MODE_MASK                0x3000
 #define MV88E6352_G1_CTL2_RMU_MODE_DISABLED    0x0000
 #define MV88E6352_G1_CTL2_RMU_MODE_PORT_4      0x1000
 #define MV88E6390_G1_CTL2_RMU_MODE_PORT_10     0x0300
 #define MV88E6390_G1_CTL2_RMU_MODE_ALL_DSA     0x0600
 #define MV88E6390_G1_CTL2_RMU_MODE_DISABLED    0x0700
+#define MV88E6390_G1_CTL2_HIST_MODE_MASK       0x00c0
+#define MV88E6390_G1_CTL2_HIST_MODE_RX         0x0040
+#define MV88E6390_G1_CTL2_HIST_MODE_TX         0x0080
+#define MV88E6352_G1_CTL2_CTR_MODE_MASK                0x0060
+#define MV88E6390_G1_CTL2_CTR_MODE             0x0020
 #define MV88E6XXX_G1_CTL2_DEVICE_NUMBER_MASK   0x001f
 
 /* Offset 0x1D: Stats Operation Register */
@@ -271,6 +277,9 @@ int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port);
 int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port);
 int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
 
+int mv88e6085_g1_ip_pri_map(struct mv88e6xxx_chip *chip);
+int mv88e6085_g1_ieee_pri_map(struct mv88e6xxx_chip *chip);
+
 int mv88e6185_g1_set_cascade_port(struct mv88e6xxx_chip *chip, int port);
 
 int mv88e6085_g1_rmu_disable(struct mv88e6xxx_chip *chip);
index f9bde01..91a3cb2 100644 (file)
@@ -1047,9 +1047,6 @@ int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
 {
        int err, irq, virq;
 
-       if (!chip->dev->of_node)
-               return -EINVAL;
-
        chip->g2_irq.domain = irq_domain_add_simple(
                chip->dev->of_node, 16, 0, &mv88e6xxx_g2_irq_domain_ops, chip);
        if (!chip->g2_irq.domain)
index 757b6d9..cdcde7f 100644 (file)
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
  * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
  * Copyright (c) 2016 John Crispin <john@phrozen.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only 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>
@@ -473,10 +465,10 @@ qca8k_set_pad_ctrl(struct qca8k_priv *priv, int port, int mode)
 static void
 qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
 {
-       u32 mask = QCA8K_PORT_STATUS_TXMAC;
+       u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
 
        /* Port 0 and 6 have no internal PHY */
-       if ((port > 0) && (port < 6))
+       if (port > 0 && port < 6)
                mask |= QCA8K_PORT_STATUS_LINK_AUTO;
 
        if (enable)
@@ -490,6 +482,7 @@ qca8k_setup(struct dsa_switch *ds)
 {
        struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
        int ret, i, phy_mode = -1;
+       u32 mask;
 
        /* Make sure that port 0 is the cpu port */
        if (!dsa_is_cpu_port(ds, 0)) {
@@ -515,7 +508,10 @@ qca8k_setup(struct dsa_switch *ds)
        if (ret < 0)
                return ret;
 
-       /* Enable CPU Port */
+       /* Enable CPU Port, force it to maximum bandwidth and full-duplex */
+       mask = QCA8K_PORT_STATUS_SPEED_1000 | QCA8K_PORT_STATUS_TXFLOW |
+              QCA8K_PORT_STATUS_RXFLOW | QCA8K_PORT_STATUS_DUPLEX;
+       qca8k_write(priv, QCA8K_REG_PORT_STATUS(QCA8K_CPU_PORT), mask);
        qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
                      QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
        qca8k_port_set_status(priv, QCA8K_CPU_PORT, 1);
@@ -583,6 +579,47 @@ qca8k_setup(struct dsa_switch *ds)
        return 0;
 }
 
+static void
+qca8k_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phy)
+{
+       struct qca8k_priv *priv = ds->priv;
+       u32 reg;
+
+       /* Force fixed-link setting for CPU port, skip others. */
+       if (!phy_is_pseudo_fixed_link(phy))
+               return;
+
+       /* Set port speed */
+       switch (phy->speed) {
+       case 10:
+               reg = QCA8K_PORT_STATUS_SPEED_10;
+               break;
+       case 100:
+               reg = QCA8K_PORT_STATUS_SPEED_100;
+               break;
+       case 1000:
+               reg = QCA8K_PORT_STATUS_SPEED_1000;
+               break;
+       default:
+               dev_dbg(priv->dev, "port%d link speed %dMbps not supported.\n",
+                       port, phy->speed);
+               return;
+       }
+
+       /* Set duplex mode */
+       if (phy->duplex == DUPLEX_FULL)
+               reg |= QCA8K_PORT_STATUS_DUPLEX;
+
+       /* Force flow control */
+       if (dsa_is_cpu_port(ds, port))
+               reg |= QCA8K_PORT_STATUS_RXFLOW | QCA8K_PORT_STATUS_TXFLOW;
+
+       /* Force link down before changing MAC options */
+       qca8k_port_set_status(priv, port, 0);
+       qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg);
+       qca8k_port_set_status(priv, port, 1);
+}
+
 static int
 qca8k_phy_read(struct dsa_switch *ds, int phy, int regnum)
 {
@@ -837,6 +874,7 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int port)
 static const struct dsa_switch_ops qca8k_switch_ops = {
        .get_tag_protocol       = qca8k_get_tag_protocol,
        .setup                  = qca8k_setup,
+       .adjust_link            = qca8k_adjust_link,
        .get_strings            = qca8k_get_strings,
        .phy_read               = qca8k_phy_read,
        .phy_write              = qca8k_phy_write,
@@ -868,6 +906,7 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
                return -ENOMEM;
 
        priv->bus = mdiodev->bus;
+       priv->dev = &mdiodev->dev;
 
        /* read the switches ID register */
        id = qca8k_read(priv, QCA8K_REG_MASK_CTRL);
@@ -939,6 +978,7 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
                         qca8k_suspend, qca8k_resume);
 
 static const struct of_device_id qca8k_of_match[] = {
+       { .compatible = "qca,qca8334" },
        { .compatible = "qca,qca8337" },
        { /* sentinel */ },
 };
index 1cf8a92..613fe5c 100644 (file)
 #define QCA8K_GOL_MAC_ADDR0                            0x60
 #define QCA8K_GOL_MAC_ADDR1                            0x64
 #define QCA8K_REG_PORT_STATUS(_i)                      (0x07c + (_i) * 4)
-#define   QCA8K_PORT_STATUS_SPEED                      GENMASK(2, 0)
-#define   QCA8K_PORT_STATUS_SPEED_S                    0
+#define   QCA8K_PORT_STATUS_SPEED                      GENMASK(1, 0)
+#define   QCA8K_PORT_STATUS_SPEED_10                   0
+#define   QCA8K_PORT_STATUS_SPEED_100                  0x1
+#define   QCA8K_PORT_STATUS_SPEED_1000                 0x2
 #define   QCA8K_PORT_STATUS_TXMAC                      BIT(2)
 #define   QCA8K_PORT_STATUS_RXMAC                      BIT(3)
 #define   QCA8K_PORT_STATUS_TXFLOW                     BIT(4)
@@ -165,6 +167,7 @@ struct qca8k_priv {
        struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
        struct dsa_switch *ds;
        struct mutex reg_mutex;
+       struct device *dev;
 };
 
 struct qca8k_mib_desc {
index cabbe22..5bc1683 100644 (file)
@@ -1209,9 +1209,9 @@ static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq,
        vp->mii.reg_num_mask = 0x1f;
 
        /* Makes sure rings are at least 16 byte aligned. */
-       vp->rx_ring = pci_alloc_consistent(pdev, sizeof(struct boom_rx_desc) * RX_RING_SIZE
+       vp->rx_ring = dma_alloc_coherent(gendev, sizeof(struct boom_rx_desc) * RX_RING_SIZE
                                           + sizeof(struct boom_tx_desc) * TX_RING_SIZE,
-                                          &vp->rx_ring_dma);
+                                          &vp->rx_ring_dma, GFP_KERNEL);
        retval = -ENOMEM;
        if (!vp->rx_ring)
                goto free_device;
@@ -1473,11 +1473,10 @@ static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq,
                return 0;
 
 free_ring:
-       pci_free_consistent(pdev,
-                                               sizeof(struct boom_rx_desc) * RX_RING_SIZE
-                                                       + sizeof(struct boom_tx_desc) * TX_RING_SIZE,
-                                               vp->rx_ring,
-                                               vp->rx_ring_dma);
+       dma_free_coherent(&pdev->dev,
+               sizeof(struct boom_rx_desc) * RX_RING_SIZE +
+               sizeof(struct boom_tx_desc) * TX_RING_SIZE,
+               vp->rx_ring, vp->rx_ring_dma);
 free_device:
        free_netdev(dev);
        pr_err(PFX "vortex_probe1 fails.  Returns %d\n", retval);
@@ -1747,9 +1746,9 @@ vortex_open(struct net_device *dev)
                                break;                  /* Bad news!  */
 
                        skb_reserve(skb, NET_IP_ALIGN); /* Align IP on 16 byte boundaries */
-                       dma = pci_map_single(VORTEX_PCI(vp), skb->data,
-                                            PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
-                       if (dma_mapping_error(&VORTEX_PCI(vp)->dev, dma))
+                       dma = dma_map_single(vp->gendev, skb->data,
+                                            PKT_BUF_SZ, DMA_FROM_DEVICE);
+                       if (dma_mapping_error(vp->gendev, dma))
                                break;
                        vp->rx_ring[i].addr = cpu_to_le32(dma);
                }
@@ -2052,9 +2051,9 @@ vortex_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (vp->bus_master) {
                /* Set the bus-master controller to transfer the packet. */
                int len = (skb->len + 3) & ~3;
-               vp->tx_skb_dma = pci_map_single(VORTEX_PCI(vp), skb->data, len,
-                                               PCI_DMA_TODEVICE);
-               if (dma_mapping_error(&VORTEX_PCI(vp)->dev, vp->tx_skb_dma)) {
+               vp->tx_skb_dma = dma_map_single(vp->gendev, skb->data, len,
+                                               DMA_TO_DEVICE);
+               if (dma_mapping_error(vp->gendev, vp->tx_skb_dma)) {
                        dev_kfree_skb_any(skb);
                        dev->stats.tx_dropped++;
                        return NETDEV_TX_OK;
@@ -2153,9 +2152,9 @@ boomerang_start_xmit(struct sk_buff *skb, struct net_device *dev)
                        vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded | AddTCPChksum | AddUDPChksum);
 
        if (!skb_shinfo(skb)->nr_frags) {
-               dma_addr = pci_map_single(VORTEX_PCI(vp), skb->data, skb->len,
-                                         PCI_DMA_TODEVICE);
-               if (dma_mapping_error(&VORTEX_PCI(vp)->dev, dma_addr))
+               dma_addr = dma_map_single(vp->gendev, skb->data, skb->len,
+                                         DMA_TO_DEVICE);
+               if (dma_mapping_error(vp->gendev, dma_addr))
                        goto out_dma_err;
 
                vp->tx_ring[entry].frag[0].addr = cpu_to_le32(dma_addr);
@@ -2163,9 +2162,9 @@ boomerang_start_xmit(struct sk_buff *skb, struct net_device *dev)
        } else {
                int i;
 
-               dma_addr = pci_map_single(VORTEX_PCI(vp), skb->data,
-                                         skb_headlen(skb), PCI_DMA_TODEVICE);
-               if (dma_mapping_error(&VORTEX_PCI(vp)->dev, dma_addr))
+               dma_addr = dma_map_single(vp->gendev, skb->data,
+                                         skb_headlen(skb), DMA_TO_DEVICE);
+               if (dma_mapping_error(vp->gendev, dma_addr))
                        goto out_dma_err;
 
                vp->tx_ring[entry].frag[0].addr = cpu_to_le32(dma_addr);
@@ -2174,21 +2173,21 @@ boomerang_start_xmit(struct sk_buff *skb, struct net_device *dev)
                for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                        skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
 
-                       dma_addr = skb_frag_dma_map(&VORTEX_PCI(vp)->dev, frag,
+                       dma_addr = skb_frag_dma_map(vp->gendev, frag,
                                                    0,
                                                    frag->size,
                                                    DMA_TO_DEVICE);
-                       if (dma_mapping_error(&VORTEX_PCI(vp)->dev, dma_addr)) {
+                       if (dma_mapping_error(vp->gendev, dma_addr)) {
                                for(i = i-1; i >= 0; i--)
-                                       dma_unmap_page(&VORTEX_PCI(vp)->dev,
+                                       dma_unmap_page(vp->gendev,
                                                       le32_to_cpu(vp->tx_ring[entry].frag[i+1].addr),
                                                       le32_to_cpu(vp->tx_ring[entry].frag[i+1].length),
                                                       DMA_TO_DEVICE);
 
-                               pci_unmap_single(VORTEX_PCI(vp),
+                               dma_unmap_single(vp->gendev,
                                                 le32_to_cpu(vp->tx_ring[entry].frag[0].addr),
                                                 le32_to_cpu(vp->tx_ring[entry].frag[0].length),
-                                                PCI_DMA_TODEVICE);
+                                                DMA_TO_DEVICE);
 
                                goto out_dma_err;
                        }
@@ -2203,8 +2202,8 @@ boomerang_start_xmit(struct sk_buff *skb, struct net_device *dev)
                }
        }
 #else
-       dma_addr = pci_map_single(VORTEX_PCI(vp), skb->data, skb->len, PCI_DMA_TODEVICE);
-       if (dma_mapping_error(&VORTEX_PCI(vp)->dev, dma_addr))
+       dma_addr = dma_map_single(vp->gendev, skb->data, skb->len, DMA_TO_DEVICE);
+       if (dma_mapping_error(vp->gendev, dma_addr))
                goto out_dma_err;
        vp->tx_ring[entry].addr = cpu_to_le32(dma_addr);
        vp->tx_ring[entry].length = cpu_to_le32(skb->len | LAST_FRAG);
@@ -2239,7 +2238,7 @@ boomerang_start_xmit(struct sk_buff *skb, struct net_device *dev)
 out:
        return NETDEV_TX_OK;
 out_dma_err:
-       dev_err(&VORTEX_PCI(vp)->dev, "Error mapping dma buffer\n");
+       dev_err(vp->gendev, "Error mapping dma buffer\n");
        goto out;
 }
 
@@ -2305,7 +2304,7 @@ _vortex_interrupt(int irq, struct net_device *dev)
                if (status & DMADone) {
                        if (ioread16(ioaddr + Wn7_MasterStatus) & 0x1000) {
                                iowrite16(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
-                               pci_unmap_single(VORTEX_PCI(vp), vp->tx_skb_dma, (vp->tx_skb->len + 3) & ~3, PCI_DMA_TODEVICE);
+                               dma_unmap_single(vp->gendev, vp->tx_skb_dma, (vp->tx_skb->len + 3) & ~3, DMA_TO_DEVICE);
                                pkts_compl++;
                                bytes_compl += vp->tx_skb->len;
                                dev_kfree_skb_irq(vp->tx_skb); /* Release the transferred buffer */
@@ -2434,19 +2433,19 @@ _boomerang_interrupt(int irq, struct net_device *dev)
                                        struct sk_buff *skb = vp->tx_skbuff[entry];
 #if DO_ZEROCOPY
                                        int i;
-                                       pci_unmap_single(VORTEX_PCI(vp),
+                                       dma_unmap_single(vp->gendev,
                                                        le32_to_cpu(vp->tx_ring[entry].frag[0].addr),
                                                        le32_to_cpu(vp->tx_ring[entry].frag[0].length)&0xFFF,
-                                                       PCI_DMA_TODEVICE);
+                                                       DMA_TO_DEVICE);
 
                                        for (i=1; i<=skb_shinfo(skb)->nr_frags; i++)
-                                                       pci_unmap_page(VORTEX_PCI(vp),
+                                                       dma_unmap_page(vp->gendev,
                                                                                         le32_to_cpu(vp->tx_ring[entry].frag[i].addr),
                                                                                         le32_to_cpu(vp->tx_ring[entry].frag[i].length)&0xFFF,
-                                                                                        PCI_DMA_TODEVICE);
+                                                                                        DMA_TO_DEVICE);
 #else
-                                       pci_unmap_single(VORTEX_PCI(vp),
-                                               le32_to_cpu(vp->tx_ring[entry].addr), skb->len, PCI_DMA_TODEVICE);
+                                       dma_unmap_single(vp->gendev,
+                                               le32_to_cpu(vp->tx_ring[entry].addr), skb->len, DMA_TO_DEVICE);
 #endif
                                        pkts_compl++;
                                        bytes_compl += skb->len;
@@ -2555,14 +2554,14 @@ static int vortex_rx(struct net_device *dev)
                                /* 'skb_put()' points to the start of sk_buff data area. */
                                if (vp->bus_master &&
                                        ! (ioread16(ioaddr + Wn7_MasterStatus) & 0x8000)) {
-                                       dma_addr_t dma = pci_map_single(VORTEX_PCI(vp), skb_put(skb, pkt_len),
-                                                                          pkt_len, PCI_DMA_FROMDEVICE);
+                                       dma_addr_t dma = dma_map_single(vp->gendev, skb_put(skb, pkt_len),
+                                                                          pkt_len, DMA_FROM_DEVICE);
                                        iowrite32(dma, ioaddr + Wn7_MasterAddr);
                                        iowrite16((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
                                        iowrite16(StartDMAUp, ioaddr + EL3_CMD);
                                        while (ioread16(ioaddr + Wn7_MasterStatus) & 0x8000)
                                                ;
-                                       pci_unmap_single(VORTEX_PCI(vp), dma, pkt_len, PCI_DMA_FROMDEVICE);
+                                       dma_unmap_single(vp->gendev, dma, pkt_len, DMA_FROM_DEVICE);
                                } else {
                                        ioread32_rep(ioaddr + RX_FIFO,
                                                     skb_put(skb, pkt_len),
@@ -2629,11 +2628,11 @@ boomerang_rx(struct net_device *dev)
                        if (pkt_len < rx_copybreak &&
                            (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) {
                                skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
-                               pci_dma_sync_single_for_cpu(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+                               dma_sync_single_for_cpu(vp->gendev, dma, PKT_BUF_SZ, DMA_FROM_DEVICE);
                                /* 'skb_put()' points to the start of sk_buff data area. */
                                skb_put_data(skb, vp->rx_skbuff[entry]->data,
                                             pkt_len);
-                               pci_dma_sync_single_for_device(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+                               dma_sync_single_for_device(vp->gendev, dma, PKT_BUF_SZ, DMA_FROM_DEVICE);
                                vp->rx_copy++;
                        } else {
                                /* Pre-allocate the replacement skb.  If it or its
@@ -2645,9 +2644,9 @@ boomerang_rx(struct net_device *dev)
                                        dev->stats.rx_dropped++;
                                        goto clear_complete;
                                }
-                               newdma = pci_map_single(VORTEX_PCI(vp), newskb->data,
-                                                       PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
-                               if (dma_mapping_error(&VORTEX_PCI(vp)->dev, newdma)) {
+                               newdma = dma_map_single(vp->gendev, newskb->data,
+                                                       PKT_BUF_SZ, DMA_FROM_DEVICE);
+                               if (dma_mapping_error(vp->gendev, newdma)) {
                                        dev->stats.rx_dropped++;
                                        consume_skb(newskb);
                                        goto clear_complete;
@@ -2658,7 +2657,7 @@ boomerang_rx(struct net_device *dev)
                                vp->rx_skbuff[entry] = newskb;
                                vp->rx_ring[entry].addr = cpu_to_le32(newdma);
                                skb_put(skb, pkt_len);
-                               pci_unmap_single(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+                               dma_unmap_single(vp->gendev, dma, PKT_BUF_SZ, DMA_FROM_DEVICE);
                                vp->rx_nocopy++;
                        }
                        skb->protocol = eth_type_trans(skb, dev);
@@ -2755,8 +2754,8 @@ vortex_close(struct net_device *dev)
        if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */
                for (i = 0; i < RX_RING_SIZE; i++)
                        if (vp->rx_skbuff[i]) {
-                               pci_unmap_single(       VORTEX_PCI(vp), le32_to_cpu(vp->rx_ring[i].addr),
-                                                                       PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+                               dma_unmap_single(vp->gendev, le32_to_cpu(vp->rx_ring[i].addr),
+                                                                       PKT_BUF_SZ, DMA_FROM_DEVICE);
                                dev_kfree_skb(vp->rx_skbuff[i]);
                                vp->rx_skbuff[i] = NULL;
                        }
@@ -2769,12 +2768,12 @@ vortex_close(struct net_device *dev)
                                int k;
 
                                for (k=0; k<=skb_shinfo(skb)->nr_frags; k++)
-                                               pci_unmap_single(VORTEX_PCI(vp),
+                                               dma_unmap_single(vp->gendev,
                                                                                 le32_to_cpu(vp->tx_ring[i].frag[k].addr),
                                                                                 le32_to_cpu(vp->tx_ring[i].frag[k].length)&0xFFF,
-                                                                                PCI_DMA_TODEVICE);
+                                                                                DMA_TO_DEVICE);
 #else
-                               pci_unmap_single(VORTEX_PCI(vp), le32_to_cpu(vp->tx_ring[i].addr), skb->len, PCI_DMA_TODEVICE);
+                               dma_unmap_single(vp->gendev, le32_to_cpu(vp->tx_ring[i].addr), skb->len, DMA_TO_DEVICE);
 #endif
                                dev_kfree_skb(skb);
                                vp->tx_skbuff[i] = NULL;
@@ -3282,11 +3281,10 @@ static void vortex_remove_one(struct pci_dev *pdev)
 
        pci_iounmap(pdev, vp->ioaddr);
 
-       pci_free_consistent(pdev,
-                                               sizeof(struct boom_rx_desc) * RX_RING_SIZE
-                                                       + sizeof(struct boom_tx_desc) * TX_RING_SIZE,
-                                               vp->rx_ring,
-                                               vp->rx_ring_dma);
+       dma_free_coherent(&pdev->dev,
+                       sizeof(struct boom_rx_desc) * RX_RING_SIZE +
+                       sizeof(struct boom_tx_desc) * TX_RING_SIZE,
+                       vp->rx_ring, vp->rx_ring_dma);
 
        pci_release_regions(pdev);
 
index ac99d08..1c97e39 100644 (file)
@@ -164,7 +164,9 @@ bad_clone_list[] __initdata = {
 #define NESM_START_PG  0x40    /* First page of TX buffer */
 #define NESM_STOP_PG   0x80    /* Last page +1 of RX ring */
 
-#if defined(CONFIG_ATARI)      /* 8-bit mode on Atari, normal on Q40 */
+#if defined(CONFIG_MACH_TX49XX)
+#  define DCR_VAL 0x48         /* 8-bit mode */
+#elif defined(CONFIG_ATARI)    /* 8-bit mode on Atari, normal on Q40 */
 #  define DCR_VAL (MACH_IS_ATARI ? 0x48 : 0x49)
 #else
 #  define DCR_VAL 0x49
index 603a570..af766fd 100644 (file)
@@ -33,9 +33,9 @@ source "drivers/net/ethernet/aquantia/Kconfig"
 source "drivers/net/ethernet/arc/Kconfig"
 source "drivers/net/ethernet/atheros/Kconfig"
 source "drivers/net/ethernet/aurora/Kconfig"
-source "drivers/net/ethernet/cadence/Kconfig"
 source "drivers/net/ethernet/broadcom/Kconfig"
 source "drivers/net/ethernet/brocade/Kconfig"
+source "drivers/net/ethernet/cadence/Kconfig"
 source "drivers/net/ethernet/calxeda/Kconfig"
 source "drivers/net/ethernet/cavium/Kconfig"
 source "drivers/net/ethernet/chelsio/Kconfig"
@@ -72,16 +72,16 @@ source "drivers/net/ethernet/dec/Kconfig"
 source "drivers/net/ethernet/dlink/Kconfig"
 source "drivers/net/ethernet/emulex/Kconfig"
 source "drivers/net/ethernet/ezchip/Kconfig"
-source "drivers/net/ethernet/neterion/Kconfig"
 source "drivers/net/ethernet/faraday/Kconfig"
 source "drivers/net/ethernet/freescale/Kconfig"
 source "drivers/net/ethernet/fujitsu/Kconfig"
 source "drivers/net/ethernet/hisilicon/Kconfig"
 source "drivers/net/ethernet/hp/Kconfig"
 source "drivers/net/ethernet/huawei/Kconfig"
+source "drivers/net/ethernet/i825xx/Kconfig"
 source "drivers/net/ethernet/ibm/Kconfig"
 source "drivers/net/ethernet/intel/Kconfig"
-source "drivers/net/ethernet/i825xx/Kconfig"
+source "drivers/net/ethernet/neterion/Kconfig"
 source "drivers/net/ethernet/xscale/Kconfig"
 
 config JME
@@ -115,6 +115,7 @@ source "drivers/net/ethernet/mellanox/Kconfig"
 source "drivers/net/ethernet/micrel/Kconfig"
 source "drivers/net/ethernet/microchip/Kconfig"
 source "drivers/net/ethernet/moxa/Kconfig"
+source "drivers/net/ethernet/mscc/Kconfig"
 source "drivers/net/ethernet/myricom/Kconfig"
 
 config FEALNX
@@ -160,20 +161,21 @@ source "drivers/net/ethernet/packetengines/Kconfig"
 source "drivers/net/ethernet/pasemi/Kconfig"
 source "drivers/net/ethernet/qlogic/Kconfig"
 source "drivers/net/ethernet/qualcomm/Kconfig"
+source "drivers/net/ethernet/rdc/Kconfig"
 source "drivers/net/ethernet/realtek/Kconfig"
 source "drivers/net/ethernet/renesas/Kconfig"
-source "drivers/net/ethernet/rdc/Kconfig"
 source "drivers/net/ethernet/rocker/Kconfig"
 source "drivers/net/ethernet/samsung/Kconfig"
 source "drivers/net/ethernet/seeq/Kconfig"
-source "drivers/net/ethernet/silan/Kconfig"
-source "drivers/net/ethernet/sis/Kconfig"
 source "drivers/net/ethernet/sfc/Kconfig"
 source "drivers/net/ethernet/sgi/Kconfig"
+source "drivers/net/ethernet/silan/Kconfig"
+source "drivers/net/ethernet/sis/Kconfig"
 source "drivers/net/ethernet/smsc/Kconfig"
 source "drivers/net/ethernet/socionext/Kconfig"
 source "drivers/net/ethernet/stmicro/Kconfig"
 source "drivers/net/ethernet/sun/Kconfig"
+source "drivers/net/ethernet/synopsys/Kconfig"
 source "drivers/net/ethernet/tehuti/Kconfig"
 source "drivers/net/ethernet/ti/Kconfig"
 source "drivers/net/ethernet/toshiba/Kconfig"
@@ -182,6 +184,5 @@ source "drivers/net/ethernet/via/Kconfig"
 source "drivers/net/ethernet/wiznet/Kconfig"
 source "drivers/net/ethernet/xilinx/Kconfig"
 source "drivers/net/ethernet/xircom/Kconfig"
-source "drivers/net/ethernet/synopsys/Kconfig"
 
 endif # ETHERNET
index 2bfd2ee..8fbfe9c 100644 (file)
@@ -55,6 +55,7 @@ obj-$(CONFIG_NET_VENDOR_MEDIATEK) += mediatek/
 obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
 obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
 obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_NET_VENDOR_MICROSEMI) += mscc/
 obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
 obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
 obj-$(CONFIG_FEALNX) += fealnx.o
index 7c204f0..24f1053 100644 (file)
@@ -1312,14 +1312,83 @@ int xgbe_powerup(struct net_device *netdev, unsigned int caller)
        return 0;
 }
 
+static void xgbe_free_memory(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+
+       /* Free the ring descriptors and buffers */
+       desc_if->free_ring_resources(pdata);
+
+       /* Free the channel and ring structures */
+       xgbe_free_channels(pdata);
+}
+
+static int xgbe_alloc_memory(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct net_device *netdev = pdata->netdev;
+       int ret;
+
+       if (pdata->new_tx_ring_count) {
+               pdata->tx_ring_count = pdata->new_tx_ring_count;
+               pdata->tx_q_count = pdata->tx_ring_count;
+               pdata->new_tx_ring_count = 0;
+       }
+
+       if (pdata->new_rx_ring_count) {
+               pdata->rx_ring_count = pdata->new_rx_ring_count;
+               pdata->new_rx_ring_count = 0;
+       }
+
+       /* Calculate the Rx buffer size before allocating rings */
+       pdata->rx_buf_size = xgbe_calc_rx_buf_size(netdev, netdev->mtu);
+
+       /* Allocate the channel and ring structures */
+       ret = xgbe_alloc_channels(pdata);
+       if (ret)
+               return ret;
+
+       /* Allocate the ring descriptors and buffers */
+       ret = desc_if->alloc_ring_resources(pdata);
+       if (ret)
+               goto err_channels;
+
+       /* Initialize the service and Tx timers */
+       xgbe_init_timers(pdata);
+
+       return 0;
+
+err_channels:
+       xgbe_free_memory(pdata);
+
+       return ret;
+}
+
 static int xgbe_start(struct xgbe_prv_data *pdata)
 {
        struct xgbe_hw_if *hw_if = &pdata->hw_if;
        struct xgbe_phy_if *phy_if = &pdata->phy_if;
        struct net_device *netdev = pdata->netdev;
+       unsigned int i;
        int ret;
 
-       DBGPR("-->xgbe_start\n");
+       /* Set the number of queues */
+       ret = netif_set_real_num_tx_queues(netdev, pdata->tx_ring_count);
+       if (ret) {
+               netdev_err(netdev, "error setting real tx queue count\n");
+               return ret;
+       }
+
+       ret = netif_set_real_num_rx_queues(netdev, pdata->rx_ring_count);
+       if (ret) {
+               netdev_err(netdev, "error setting real rx queue count\n");
+               return ret;
+       }
+
+       /* Set RSS lookup table data for programming */
+       for (i = 0; i < XGBE_RSS_MAX_TABLE_SIZE; i++)
+               XGMAC_SET_BITS(pdata->rss_table[i], MAC_RSSDR, DMCH,
+                              i % pdata->rx_ring_count);
 
        ret = hw_if->init(pdata);
        if (ret)
@@ -1347,8 +1416,6 @@ static int xgbe_start(struct xgbe_prv_data *pdata)
 
        clear_bit(XGBE_STOPPED, &pdata->dev_state);
 
-       DBGPR("<--xgbe_start\n");
-
        return 0;
 
 err_irqs:
@@ -1426,10 +1493,22 @@ static void xgbe_stopdev(struct work_struct *work)
        netdev_alert(pdata->netdev, "device stopped\n");
 }
 
-static void xgbe_restart_dev(struct xgbe_prv_data *pdata)
+void xgbe_full_restart_dev(struct xgbe_prv_data *pdata)
 {
-       DBGPR("-->xgbe_restart_dev\n");
+       /* If not running, "restart" will happen on open */
+       if (!netif_running(pdata->netdev))
+               return;
+
+       xgbe_stop(pdata);
+
+       xgbe_free_memory(pdata);
+       xgbe_alloc_memory(pdata);
+
+       xgbe_start(pdata);
+}
 
+void xgbe_restart_dev(struct xgbe_prv_data *pdata)
+{
        /* If not running, "restart" will happen on open */
        if (!netif_running(pdata->netdev))
                return;
@@ -1440,8 +1519,6 @@ static void xgbe_restart_dev(struct xgbe_prv_data *pdata)
        xgbe_free_rx_data(pdata);
 
        xgbe_start(pdata);
-
-       DBGPR("<--xgbe_restart_dev\n");
 }
 
 static void xgbe_restart(struct work_struct *work)
@@ -1827,11 +1904,8 @@ static void xgbe_packet_info(struct xgbe_prv_data *pdata,
 static int xgbe_open(struct net_device *netdev)
 {
        struct xgbe_prv_data *pdata = netdev_priv(netdev);
-       struct xgbe_desc_if *desc_if = &pdata->desc_if;
        int ret;
 
-       DBGPR("-->xgbe_open\n");
-
        /* Create the various names based on netdev name */
        snprintf(pdata->an_name, sizeof(pdata->an_name) - 1, "%s-pcs",
                 netdev_name(netdev));
@@ -1876,43 +1950,25 @@ static int xgbe_open(struct net_device *netdev)
                goto err_sysclk;
        }
 
-       /* Calculate the Rx buffer size before allocating rings */
-       ret = xgbe_calc_rx_buf_size(netdev, netdev->mtu);
-       if (ret < 0)
-               goto err_ptpclk;
-       pdata->rx_buf_size = ret;
-
-       /* Allocate the channel and ring structures */
-       ret = xgbe_alloc_channels(pdata);
-       if (ret)
-               goto err_ptpclk;
-
-       /* Allocate the ring descriptors and buffers */
-       ret = desc_if->alloc_ring_resources(pdata);
-       if (ret)
-               goto err_channels;
-
        INIT_WORK(&pdata->service_work, xgbe_service);
        INIT_WORK(&pdata->restart_work, xgbe_restart);
        INIT_WORK(&pdata->stopdev_work, xgbe_stopdev);
        INIT_WORK(&pdata->tx_tstamp_work, xgbe_tx_tstamp);
-       xgbe_init_timers(pdata);
+
+       ret = xgbe_alloc_memory(pdata);
+       if (ret)
+               goto err_ptpclk;
 
        ret = xgbe_start(pdata);
        if (ret)
-               goto err_rings;
+               goto err_mem;
 
        clear_bit(XGBE_DOWN, &pdata->dev_state);
 
-       DBGPR("<--xgbe_open\n");
-
        return 0;
 
-err_rings:
-       desc_if->free_ring_resources(pdata);
-
-err_channels:
-       xgbe_free_channels(pdata);
+err_mem:
+       xgbe_free_memory(pdata);
 
 err_ptpclk:
        clk_disable_unprepare(pdata->ptpclk);
@@ -1932,18 +1988,11 @@ err_dev_wq:
 static int xgbe_close(struct net_device *netdev)
 {
        struct xgbe_prv_data *pdata = netdev_priv(netdev);
-       struct xgbe_desc_if *desc_if = &pdata->desc_if;
-
-       DBGPR("-->xgbe_close\n");
 
        /* Stop the device */
        xgbe_stop(pdata);
 
-       /* Free the ring descriptors and buffers */
-       desc_if->free_ring_resources(pdata);
-
-       /* Free the channel and ring structures */
-       xgbe_free_channels(pdata);
+       xgbe_free_memory(pdata);
 
        /* Disable the clocks */
        clk_disable_unprepare(pdata->ptpclk);
@@ -1957,8 +2006,6 @@ static int xgbe_close(struct net_device *netdev)
 
        set_bit(XGBE_DOWN, &pdata->dev_state);
 
-       DBGPR("<--xgbe_close\n");
-
        return 0;
 }
 
index ff397bb..a880f10 100644 (file)
@@ -626,6 +626,217 @@ static int xgbe_get_ts_info(struct net_device *netdev,
        return 0;
 }
 
+static int xgbe_get_module_info(struct net_device *netdev,
+                               struct ethtool_modinfo *modinfo)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       return pdata->phy_if.module_info(pdata, modinfo);
+}
+
+static int xgbe_get_module_eeprom(struct net_device *netdev,
+                                 struct ethtool_eeprom *eeprom, u8 *data)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       return pdata->phy_if.module_eeprom(pdata, eeprom, data);
+}
+
+static void xgbe_get_ringparam(struct net_device *netdev,
+                              struct ethtool_ringparam *ringparam)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       ringparam->rx_max_pending = XGBE_RX_DESC_CNT_MAX;
+       ringparam->tx_max_pending = XGBE_TX_DESC_CNT_MAX;
+       ringparam->rx_pending = pdata->rx_desc_count;
+       ringparam->tx_pending = pdata->tx_desc_count;
+}
+
+static int xgbe_set_ringparam(struct net_device *netdev,
+                             struct ethtool_ringparam *ringparam)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       unsigned int rx, tx;
+
+       if (ringparam->rx_mini_pending || ringparam->rx_jumbo_pending) {
+               netdev_err(netdev, "unsupported ring parameter\n");
+               return -EINVAL;
+       }
+
+       if ((ringparam->rx_pending < XGBE_RX_DESC_CNT_MIN) ||
+           (ringparam->rx_pending > XGBE_RX_DESC_CNT_MAX)) {
+               netdev_err(netdev,
+                          "rx ring parameter must be between %u and %u\n",
+                          XGBE_RX_DESC_CNT_MIN, XGBE_RX_DESC_CNT_MAX);
+               return -EINVAL;
+       }
+
+       if ((ringparam->tx_pending < XGBE_TX_DESC_CNT_MIN) ||
+           (ringparam->tx_pending > XGBE_TX_DESC_CNT_MAX)) {
+               netdev_err(netdev,
+                          "tx ring parameter must be between %u and %u\n",
+                          XGBE_TX_DESC_CNT_MIN, XGBE_TX_DESC_CNT_MAX);
+               return -EINVAL;
+       }
+
+       rx = __rounddown_pow_of_two(ringparam->rx_pending);
+       if (rx != ringparam->rx_pending)
+               netdev_notice(netdev,
+                             "rx ring parameter rounded to power of two: %u\n",
+                             rx);
+
+       tx = __rounddown_pow_of_two(ringparam->tx_pending);
+       if (tx != ringparam->tx_pending)
+               netdev_notice(netdev,
+                             "tx ring parameter rounded to power of two: %u\n",
+                             tx);
+
+       if ((rx == pdata->rx_desc_count) &&
+           (tx == pdata->tx_desc_count))
+               goto out;
+
+       pdata->rx_desc_count = rx;
+       pdata->tx_desc_count = tx;
+
+       xgbe_restart_dev(pdata);
+
+out:
+       return 0;
+}
+
+static void xgbe_get_channels(struct net_device *netdev,
+                             struct ethtool_channels *channels)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       unsigned int rx, tx, combined;
+
+       /* Calculate maximums allowed:
+        *   - Take into account the number of available IRQs
+        *   - Do not take into account the number of online CPUs so that
+        *     the user can over-subscribe if desired
+        *   - Tx is additionally limited by the number of hardware queues
+        */
+       rx = min(pdata->hw_feat.rx_ch_cnt, pdata->rx_max_channel_count);
+       rx = min(rx, pdata->channel_irq_count);
+       tx = min(pdata->hw_feat.tx_ch_cnt, pdata->tx_max_channel_count);
+       tx = min(tx, pdata->channel_irq_count);
+       tx = min(tx, pdata->tx_max_q_count);
+
+       combined = min(rx, tx);
+
+       channels->max_combined = combined;
+       channels->max_rx = rx ? rx - 1 : 0;
+       channels->max_tx = tx ? tx - 1 : 0;
+
+       /* Get current settings based on device state */
+       rx = pdata->new_rx_ring_count ? : pdata->rx_ring_count;
+       tx = pdata->new_tx_ring_count ? : pdata->tx_ring_count;
+
+       combined = min(rx, tx);
+       rx -= combined;
+       tx -= combined;
+
+       channels->combined_count = combined;
+       channels->rx_count = rx;
+       channels->tx_count = tx;
+}
+
+static void xgbe_print_set_channels_input(struct net_device *netdev,
+                                         struct ethtool_channels *channels)
+{
+       netdev_err(netdev, "channel inputs: combined=%u, rx-only=%u, tx-only=%u\n",
+                  channels->combined_count, channels->rx_count,
+                  channels->tx_count);
+}
+
+static int xgbe_set_channels(struct net_device *netdev,
+                            struct ethtool_channels *channels)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       unsigned int rx, rx_curr, tx, tx_curr, combined;
+
+       /* Calculate maximums allowed:
+        *   - Take into account the number of available IRQs
+        *   - Do not take into account the number of online CPUs so that
+        *     the user can over-subscribe if desired
+        *   - Tx is additionally limited by the number of hardware queues
+        */
+       rx = min(pdata->hw_feat.rx_ch_cnt, pdata->rx_max_channel_count);
+       rx = min(rx, pdata->channel_irq_count);
+       tx = min(pdata->hw_feat.tx_ch_cnt, pdata->tx_max_channel_count);
+       tx = min(tx, pdata->tx_max_q_count);
+       tx = min(tx, pdata->channel_irq_count);
+
+       combined = min(rx, tx);
+
+       /* Should not be setting other count */
+       if (channels->other_count) {
+               netdev_err(netdev,
+                          "other channel count must be zero\n");
+               return -EINVAL;
+       }
+
+       /* Require at least one Combined (Rx and Tx) channel */
+       if (!channels->combined_count) {
+               netdev_err(netdev,
+                          "at least one combined Rx/Tx channel is required\n");
+               xgbe_print_set_channels_input(netdev, channels);
+               return -EINVAL;
+       }
+
+       /* Check combined channels */
+       if (channels->combined_count > combined) {
+               netdev_err(netdev,
+                          "combined channel count cannot exceed %u\n",
+                          combined);
+               xgbe_print_set_channels_input(netdev, channels);
+               return -EINVAL;
+       }
+
+       /* Can have some Rx-only or Tx-only channels, but not both */
+       if (channels->rx_count && channels->tx_count) {
+               netdev_err(netdev,
+                          "cannot specify both Rx-only and Tx-only channels\n");
+               xgbe_print_set_channels_input(netdev, channels);
+               return -EINVAL;
+       }
+
+       /* Check that we don't exceed the maximum number of channels */
+       if ((channels->combined_count + channels->rx_count) > rx) {
+               netdev_err(netdev,
+                          "total Rx channels (%u) requested exceeds maximum available (%u)\n",
+                          channels->combined_count + channels->rx_count, rx);
+               xgbe_print_set_channels_input(netdev, channels);
+               return -EINVAL;
+       }
+
+       if ((channels->combined_count + channels->tx_count) > tx) {
+               netdev_err(netdev,
+                          "total Tx channels (%u) requested exceeds maximum available (%u)\n",
+                          channels->combined_count + channels->tx_count, tx);
+               xgbe_print_set_channels_input(netdev, channels);
+               return -EINVAL;
+       }
+
+       rx = channels->combined_count + channels->rx_count;
+       tx = channels->combined_count + channels->tx_count;
+
+       rx_curr = pdata->new_rx_ring_count ? : pdata->rx_ring_count;
+       tx_curr = pdata->new_tx_ring_count ? : pdata->tx_ring_count;
+
+       if ((rx == rx_curr) && (tx == tx_curr))
+               goto out;
+
+       pdata->new_rx_ring_count = rx;
+       pdata->new_tx_ring_count = tx;
+
+       xgbe_full_restart_dev(pdata);
+
+out:
+       return 0;
+}
+
 static const struct ethtool_ops xgbe_ethtool_ops = {
        .get_drvinfo = xgbe_get_drvinfo,
        .get_msglevel = xgbe_get_msglevel,
@@ -646,6 +857,12 @@ static const struct ethtool_ops xgbe_ethtool_ops = {
        .get_ts_info = xgbe_get_ts_info,
        .get_link_ksettings = xgbe_get_link_ksettings,
        .set_link_ksettings = xgbe_set_link_ksettings,
+       .get_module_info = xgbe_get_module_info,
+       .get_module_eeprom = xgbe_get_module_eeprom,
+       .get_ringparam = xgbe_get_ringparam,
+       .set_ringparam = xgbe_set_ringparam,
+       .get_channels = xgbe_get_channels,
+       .set_channels = xgbe_set_channels,
 };
 
 const struct ethtool_ops *xgbe_get_ethtool_ops(void)
index 441d097..b41f236 100644 (file)
@@ -265,7 +265,6 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata)
 {
        struct net_device *netdev = pdata->netdev;
        struct device *dev = pdata->dev;
-       unsigned int i;
        int ret;
 
        netdev->irq = pdata->dev_irq;
@@ -324,26 +323,9 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata)
                                pdata->tx_ring_count, pdata->rx_ring_count);
        }
 
-       /* Set the number of queues */
-       ret = netif_set_real_num_tx_queues(netdev, pdata->tx_ring_count);
-       if (ret) {
-               dev_err(dev, "error setting real tx queue count\n");
-               return ret;
-       }
-
-       ret = netif_set_real_num_rx_queues(netdev, pdata->rx_ring_count);
-       if (ret) {
-               dev_err(dev, "error setting real rx queue count\n");
-               return ret;
-       }
-
-       /* Initialize RSS hash key and lookup table */
+       /* Initialize RSS hash key */
        netdev_rss_key_fill(pdata->rss_key, sizeof(pdata->rss_key));
 
-       for (i = 0; i < XGBE_RSS_MAX_TABLE_SIZE; i++)
-               XGMAC_SET_BITS(pdata->rss_table[i], MAC_RSSDR, DMCH,
-                              i % pdata->rx_ring_count);
-
        XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, IP2TE, 1);
        XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, TCP4TE, 1);
        XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, UDP4TE, 1);
index 1b45cd7..4b5d625 100644 (file)
 #include "xgbe.h"
 #include "xgbe-common.h"
 
+static int xgbe_phy_module_eeprom(struct xgbe_prv_data *pdata,
+                                 struct ethtool_eeprom *eeprom, u8 *data)
+{
+       if (!pdata->phy_if.phy_impl.module_eeprom)
+               return -ENXIO;
+
+       return pdata->phy_if.phy_impl.module_eeprom(pdata, eeprom, data);
+}
+
+static int xgbe_phy_module_info(struct xgbe_prv_data *pdata,
+                               struct ethtool_modinfo *modinfo)
+{
+       if (!pdata->phy_if.phy_impl.module_info)
+               return -ENXIO;
+
+       return pdata->phy_if.phy_impl.module_info(pdata, modinfo);
+}
+
 static void xgbe_an37_clear_interrupts(struct xgbe_prv_data *pdata)
 {
        int reg;
@@ -198,31 +216,8 @@ static void xgbe_an_clear_interrupts_all(struct xgbe_prv_data *pdata)
        xgbe_an37_clear_interrupts(pdata);
 }
 
-static void xgbe_an73_enable_kr_training(struct xgbe_prv_data *pdata)
-{
-       unsigned int reg;
-
-       reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
-
-       reg |= XGBE_KR_TRAINING_ENABLE;
-       XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg);
-}
-
-static void xgbe_an73_disable_kr_training(struct xgbe_prv_data *pdata)
-{
-       unsigned int reg;
-
-       reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
-
-       reg &= ~XGBE_KR_TRAINING_ENABLE;
-       XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg);
-}
-
 static void xgbe_kr_mode(struct xgbe_prv_data *pdata)
 {
-       /* Enable KR training */
-       xgbe_an73_enable_kr_training(pdata);
-
        /* Set MAC to 10G speed */
        pdata->hw_if.set_speed(pdata, SPEED_10000);
 
@@ -232,9 +227,6 @@ static void xgbe_kr_mode(struct xgbe_prv_data *pdata)
 
 static void xgbe_kx_2500_mode(struct xgbe_prv_data *pdata)
 {
-       /* Disable KR training */
-       xgbe_an73_disable_kr_training(pdata);
-
        /* Set MAC to 2.5G speed */
        pdata->hw_if.set_speed(pdata, SPEED_2500);
 
@@ -244,9 +236,6 @@ static void xgbe_kx_2500_mode(struct xgbe_prv_data *pdata)
 
 static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata)
 {
-       /* Disable KR training */
-       xgbe_an73_disable_kr_training(pdata);
-
        /* Set MAC to 1G speed */
        pdata->hw_if.set_speed(pdata, SPEED_1000);
 
@@ -260,9 +249,6 @@ static void xgbe_sfi_mode(struct xgbe_prv_data *pdata)
        if (pdata->kr_redrv)
                return xgbe_kr_mode(pdata);
 
-       /* Disable KR training */
-       xgbe_an73_disable_kr_training(pdata);
-
        /* Set MAC to 10G speed */
        pdata->hw_if.set_speed(pdata, SPEED_10000);
 
@@ -272,9 +258,6 @@ static void xgbe_sfi_mode(struct xgbe_prv_data *pdata)
 
 static void xgbe_x_mode(struct xgbe_prv_data *pdata)
 {
-       /* Disable KR training */
-       xgbe_an73_disable_kr_training(pdata);
-
        /* Set MAC to 1G speed */
        pdata->hw_if.set_speed(pdata, SPEED_1000);
 
@@ -284,9 +267,6 @@ static void xgbe_x_mode(struct xgbe_prv_data *pdata)
 
 static void xgbe_sgmii_1000_mode(struct xgbe_prv_data *pdata)
 {
-       /* Disable KR training */
-       xgbe_an73_disable_kr_training(pdata);
-
        /* Set MAC to 1G speed */
        pdata->hw_if.set_speed(pdata, SPEED_1000);
 
@@ -296,9 +276,6 @@ static void xgbe_sgmii_1000_mode(struct xgbe_prv_data *pdata)
 
 static void xgbe_sgmii_100_mode(struct xgbe_prv_data *pdata)
 {
-       /* Disable KR training */
-       xgbe_an73_disable_kr_training(pdata);
-
        /* Set MAC to 1G speed */
        pdata->hw_if.set_speed(pdata, SPEED_1000);
 
@@ -354,13 +331,15 @@ static void xgbe_switch_mode(struct xgbe_prv_data *pdata)
        xgbe_change_mode(pdata, pdata->phy_if.phy_impl.switch_mode(pdata));
 }
 
-static void xgbe_set_mode(struct xgbe_prv_data *pdata,
+static bool xgbe_set_mode(struct xgbe_prv_data *pdata,
                          enum xgbe_mode mode)
 {
        if (mode == xgbe_cur_mode(pdata))
-               return;
+               return false;
 
        xgbe_change_mode(pdata, mode);
+
+       return true;
 }
 
 static bool xgbe_use_mode(struct xgbe_prv_data *pdata,
@@ -407,6 +386,12 @@ static void xgbe_an73_set(struct xgbe_prv_data *pdata, bool enable,
 {
        unsigned int reg;
 
+       /* Disable KR training for now */
+       reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
+       reg &= ~XGBE_KR_TRAINING_ENABLE;
+       XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg);
+
+       /* Update AN settings */
        reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1);
        reg &= ~MDIO_AN_CTRL1_ENABLE;
 
@@ -504,21 +489,19 @@ static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata,
        XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FECCTRL, reg);
 
        /* Start KR training */
-       reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
-       if (reg & XGBE_KR_TRAINING_ENABLE) {
-               if (pdata->phy_if.phy_impl.kr_training_pre)
-                       pdata->phy_if.phy_impl.kr_training_pre(pdata);
+       if (pdata->phy_if.phy_impl.kr_training_pre)
+               pdata->phy_if.phy_impl.kr_training_pre(pdata);
 
-               reg |= XGBE_KR_TRAINING_START;
-               XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL,
-                           reg);
+       reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
+       reg |= XGBE_KR_TRAINING_ENABLE;
+       reg |= XGBE_KR_TRAINING_START;
+       XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg);
 
-               netif_dbg(pdata, link, pdata->netdev,
-                         "KR training initiated\n");
+       netif_dbg(pdata, link, pdata->netdev,
+                 "KR training initiated\n");
 
-               if (pdata->phy_if.phy_impl.kr_training_post)
-                       pdata->phy_if.phy_impl.kr_training_post(pdata);
-       }
+       if (pdata->phy_if.phy_impl.kr_training_post)
+               pdata->phy_if.phy_impl.kr_training_post(pdata);
 
        return XGBE_AN_PAGE_RECEIVED;
 }
@@ -1197,21 +1180,23 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata)
        return 0;
 }
 
-static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
+static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata, bool set_mode)
 {
        int ret;
 
+       mutex_lock(&pdata->an_mutex);
+
        set_bit(XGBE_LINK_INIT, &pdata->dev_state);
        pdata->link_check = jiffies;
 
        ret = pdata->phy_if.phy_impl.an_config(pdata);
        if (ret)
-               return ret;
+               goto out;
 
        if (pdata->phy.autoneg != AUTONEG_ENABLE) {
                ret = xgbe_phy_config_fixed(pdata);
                if (ret || !pdata->kr_redrv)
-                       return ret;
+                       goto out;
 
                netif_dbg(pdata, link, pdata->netdev, "AN redriver support\n");
        } else {
@@ -1221,24 +1206,27 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
        /* Disable auto-negotiation interrupt */
        disable_irq(pdata->an_irq);
 
-       /* Start auto-negotiation in a supported mode */
-       if (xgbe_use_mode(pdata, XGBE_MODE_KR)) {
-               xgbe_set_mode(pdata, XGBE_MODE_KR);
-       } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) {
-               xgbe_set_mode(pdata, XGBE_MODE_KX_2500);
-       } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) {
-               xgbe_set_mode(pdata, XGBE_MODE_KX_1000);
-       } else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) {
-               xgbe_set_mode(pdata, XGBE_MODE_SFI);
-       } else if (xgbe_use_mode(pdata, XGBE_MODE_X)) {
-               xgbe_set_mode(pdata, XGBE_MODE_X);
-       } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) {
-               xgbe_set_mode(pdata, XGBE_MODE_SGMII_1000);
-       } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) {
-               xgbe_set_mode(pdata, XGBE_MODE_SGMII_100);
-       } else {
-               enable_irq(pdata->an_irq);
-               return -EINVAL;
+       if (set_mode) {
+               /* Start auto-negotiation in a supported mode */
+               if (xgbe_use_mode(pdata, XGBE_MODE_KR)) {
+                       xgbe_set_mode(pdata, XGBE_MODE_KR);
+               } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) {
+                       xgbe_set_mode(pdata, XGBE_MODE_KX_2500);
+               } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) {
+                       xgbe_set_mode(pdata, XGBE_MODE_KX_1000);
+               } else if (xgbe_use_mode(pdata, XGBE_MODE_SFI)) {
+                       xgbe_set_mode(pdata, XGBE_MODE_SFI);
+               } else if (xgbe_use_mode(pdata, XGBE_MODE_X)) {
+                       xgbe_set_mode(pdata, XGBE_MODE_X);
+               } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_1000)) {
+                       xgbe_set_mode(pdata, XGBE_MODE_SGMII_1000);
+               } else if (xgbe_use_mode(pdata, XGBE_MODE_SGMII_100)) {
+                       xgbe_set_mode(pdata, XGBE_MODE_SGMII_100);
+               } else {
+                       enable_irq(pdata->an_irq);
+                       ret = -EINVAL;
+                       goto out;
+               }
        }
 
        /* Disable and stop any in progress auto-negotiation */
@@ -1258,16 +1246,7 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
        xgbe_an_init(pdata);
        xgbe_an_restart(pdata);
 
-       return 0;
-}
-
-static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
-{
-       int ret;
-
-       mutex_lock(&pdata->an_mutex);
-
-       ret = __xgbe_phy_config_aneg(pdata);
+out:
        if (ret)
                set_bit(XGBE_LINK_ERR, &pdata->dev_state);
        else
@@ -1278,6 +1257,16 @@ static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
        return ret;
 }
 
+static int xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
+{
+       return __xgbe_phy_config_aneg(pdata, true);
+}
+
+static int xgbe_phy_reconfig_aneg(struct xgbe_prv_data *pdata)
+{
+       return __xgbe_phy_config_aneg(pdata, false);
+}
+
 static bool xgbe_phy_aneg_done(struct xgbe_prv_data *pdata)
 {
        return (pdata->an_result == XGBE_AN_COMPLETE);
@@ -1334,7 +1323,8 @@ static void xgbe_phy_status_result(struct xgbe_prv_data *pdata)
 
        pdata->phy.duplex = DUPLEX_FULL;
 
-       xgbe_set_mode(pdata, mode);
+       if (xgbe_set_mode(pdata, mode) && pdata->an_again)
+               xgbe_phy_reconfig_aneg(pdata);
 }
 
 static void xgbe_phy_status(struct xgbe_prv_data *pdata)
@@ -1639,4 +1629,7 @@ void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if)
        phy_if->phy_valid_speed = xgbe_phy_valid_speed;
 
        phy_if->an_isr          = xgbe_an_combined_isr;
+
+       phy_if->module_info     = xgbe_phy_module_info;
+       phy_if->module_eeprom   = xgbe_phy_module_eeprom;
 }
index 82d1f41..7b86240 100644 (file)
@@ -335,16 +335,33 @@ static int xgbe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        pdata->awcr = XGBE_DMA_PCI_AWCR;
        pdata->awarcr = XGBE_DMA_PCI_AWARCR;
 
+       /* Read the port property registers */
+       pdata->pp0 = XP_IOREAD(pdata, XP_PROP_0);
+       pdata->pp1 = XP_IOREAD(pdata, XP_PROP_1);
+       pdata->pp2 = XP_IOREAD(pdata, XP_PROP_2);
+       pdata->pp3 = XP_IOREAD(pdata, XP_PROP_3);
+       pdata->pp4 = XP_IOREAD(pdata, XP_PROP_4);
+       if (netif_msg_probe(pdata)) {
+               dev_dbg(dev, "port property 0 = %#010x\n", pdata->pp0);
+               dev_dbg(dev, "port property 1 = %#010x\n", pdata->pp1);
+               dev_dbg(dev, "port property 2 = %#010x\n", pdata->pp2);
+               dev_dbg(dev, "port property 3 = %#010x\n", pdata->pp3);
+               dev_dbg(dev, "port property 4 = %#010x\n", pdata->pp4);
+       }
+
        /* Set the maximum channels and queues */
-       reg = XP_IOREAD(pdata, XP_PROP_1);
-       pdata->tx_max_channel_count = XP_GET_BITS(reg, XP_PROP_1, MAX_TX_DMA);
-       pdata->rx_max_channel_count = XP_GET_BITS(reg, XP_PROP_1, MAX_RX_DMA);
-       pdata->tx_max_q_count = XP_GET_BITS(reg, XP_PROP_1, MAX_TX_QUEUES);
-       pdata->rx_max_q_count = XP_GET_BITS(reg, XP_PROP_1, MAX_RX_QUEUES);
+       pdata->tx_max_channel_count = XP_GET_BITS(pdata->pp1, XP_PROP_1,
+                                                 MAX_TX_DMA);
+       pdata->rx_max_channel_count = XP_GET_BITS(pdata->pp1, XP_PROP_1,
+                                                 MAX_RX_DMA);
+       pdata->tx_max_q_count = XP_GET_BITS(pdata->pp1, XP_PROP_1,
+                                           MAX_TX_QUEUES);
+       pdata->rx_max_q_count = XP_GET_BITS(pdata->pp1, XP_PROP_1,
+                                           MAX_RX_QUEUES);
        if (netif_msg_probe(pdata)) {
                dev_dbg(dev, "max tx/rx channel count = %u/%u\n",
                        pdata->tx_max_channel_count,
-                       pdata->tx_max_channel_count);
+                       pdata->rx_max_channel_count);
                dev_dbg(dev, "max tx/rx hw queue count = %u/%u\n",
                        pdata->tx_max_q_count, pdata->rx_max_q_count);
        }
@@ -353,12 +370,13 @@ static int xgbe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        xgbe_set_counts(pdata);
 
        /* Set the maximum fifo amounts */
-       reg = XP_IOREAD(pdata, XP_PROP_2);
-       pdata->tx_max_fifo_size = XP_GET_BITS(reg, XP_PROP_2, TX_FIFO_SIZE);
+       pdata->tx_max_fifo_size = XP_GET_BITS(pdata->pp2, XP_PROP_2,
+                                             TX_FIFO_SIZE);
        pdata->tx_max_fifo_size *= 16384;
        pdata->tx_max_fifo_size = min(pdata->tx_max_fifo_size,
                                      pdata->vdata->tx_max_fifo_size);
-       pdata->rx_max_fifo_size = XP_GET_BITS(reg, XP_PROP_2, RX_FIFO_SIZE);
+       pdata->rx_max_fifo_size = XP_GET_BITS(pdata->pp2, XP_PROP_2,
+                                             RX_FIFO_SIZE);
        pdata->rx_max_fifo_size *= 16384;
        pdata->rx_max_fifo_size = min(pdata->rx_max_fifo_size,
                                      pdata->vdata->rx_max_fifo_size);
index aac8843..3ceb4f9 100644 (file)
 #include <linux/kmod.h>
 #include <linux/mdio.h>
 #include <linux/phy.h>
+#include <linux/ethtool.h>
 
 #include "xgbe.h"
 #include "xgbe-common.h"
@@ -270,6 +271,15 @@ struct xgbe_sfp_eeprom {
        u8 vendor[32];
 };
 
+#define XGBE_SFP_DIAGS_SUPPORTED(_x)                   \
+       ((_x)->extd[XGBE_SFP_EXTD_SFF_8472] &&          \
+        !((_x)->extd[XGBE_SFP_EXTD_DIAG] & XGBE_SFP_EXTD_DIAG_ADDR_CHANGE))
+
+#define XGBE_SFP_EEPROM_BASE_LEN       256
+#define XGBE_SFP_EEPROM_DIAG_LEN       256
+#define XGBE_SFP_EEPROM_MAX            (XGBE_SFP_EEPROM_BASE_LEN +     \
+                                        XGBE_SFP_EEPROM_DIAG_LEN)
+
 #define XGBE_BEL_FUSE_VENDOR   "BEL-FUSE        "
 #define XGBE_BEL_FUSE_PARTNO   "1GBT-SFP06      "
 
@@ -327,8 +337,6 @@ struct xgbe_phy_data {
 
        unsigned int mdio_addr;
 
-       unsigned int comm_owned;
-
        /* SFP Support */
        enum xgbe_sfp_comm sfp_comm;
        unsigned int sfp_mux_address;
@@ -345,7 +353,6 @@ struct xgbe_phy_data {
        unsigned int sfp_rx_los;
        unsigned int sfp_tx_fault;
        unsigned int sfp_mod_absent;
-       unsigned int sfp_diags;
        unsigned int sfp_changed;
        unsigned int sfp_phy_avail;
        unsigned int sfp_cable_len;
@@ -382,12 +389,6 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata);
 static int xgbe_phy_i2c_xfer(struct xgbe_prv_data *pdata,
                             struct xgbe_i2c_op *i2c_op)
 {
-       struct xgbe_phy_data *phy_data = pdata->phy_data;
-
-       /* Be sure we own the bus */
-       if (WARN_ON(!phy_data->comm_owned))
-               return -EIO;
-
        return pdata->i2c_if.i2c_xfer(pdata, i2c_op);
 }
 
@@ -549,10 +550,6 @@ static int xgbe_phy_sfp_get_mux(struct xgbe_prv_data *pdata)
 
 static void xgbe_phy_put_comm_ownership(struct xgbe_prv_data *pdata)
 {
-       struct xgbe_phy_data *phy_data = pdata->phy_data;
-
-       phy_data->comm_owned = 0;
-
        mutex_unlock(&xgbe_phy_comm_lock);
 }
 
@@ -562,9 +559,6 @@ static int xgbe_phy_get_comm_ownership(struct xgbe_prv_data *pdata)
        unsigned long timeout;
        unsigned int mutex_id;
 
-       if (phy_data->comm_owned)
-               return 0;
-
        /* The I2C and MDIO/GPIO bus is multiplexed between multiple devices,
         * the driver needs to take the software mutex and then the hardware
         * mutexes before being able to use the busses.
@@ -593,7 +587,6 @@ static int xgbe_phy_get_comm_ownership(struct xgbe_prv_data *pdata)
                XP_IOWRITE(pdata, XP_I2C_MUTEX, mutex_id);
                XP_IOWRITE(pdata, XP_MDIO_MUTEX, mutex_id);
 
-               phy_data->comm_owned = 1;
                return 0;
        }
 
@@ -867,6 +860,9 @@ static bool xgbe_phy_finisar_phy_quirks(struct xgbe_prv_data *pdata)
        struct xgbe_phy_data *phy_data = pdata->phy_data;
        unsigned int phy_id = phy_data->phydev->phy_id;
 
+       if (phy_data->port_mode != XGBE_PORT_MODE_SFP)
+               return false;
+
        if ((phy_id & 0xfffffff0) != 0x01ff0cc0)
                return false;
 
@@ -892,8 +888,83 @@ static bool xgbe_phy_finisar_phy_quirks(struct xgbe_prv_data *pdata)
        return true;
 }
 
+static bool xgbe_phy_belfuse_phy_quirks(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       struct xgbe_sfp_eeprom *sfp_eeprom = &phy_data->sfp_eeprom;
+       unsigned int phy_id = phy_data->phydev->phy_id;
+       int reg;
+
+       if (phy_data->port_mode != XGBE_PORT_MODE_SFP)
+               return false;
+
+       if (memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_NAME],
+                  XGBE_BEL_FUSE_VENDOR, XGBE_SFP_BASE_VENDOR_NAME_LEN))
+               return false;
+
+       /* For Bel-Fuse, use the extra AN flag */
+       pdata->an_again = 1;
+
+       if (memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_PN],
+                  XGBE_BEL_FUSE_PARTNO, XGBE_SFP_BASE_VENDOR_PN_LEN))
+               return false;
+
+       if ((phy_id & 0xfffffff0) != 0x03625d10)
+               return false;
+
+       /* Disable RGMII mode */
+       phy_write(phy_data->phydev, 0x18, 0x7007);
+       reg = phy_read(phy_data->phydev, 0x18);
+       phy_write(phy_data->phydev, 0x18, reg & ~0x0080);
+
+       /* Enable fiber register bank */
+       phy_write(phy_data->phydev, 0x1c, 0x7c00);
+       reg = phy_read(phy_data->phydev, 0x1c);
+       reg &= 0x03ff;
+       reg &= ~0x0001;
+       phy_write(phy_data->phydev, 0x1c, 0x8000 | 0x7c00 | reg | 0x0001);
+
+       /* Power down SerDes */
+       reg = phy_read(phy_data->phydev, 0x00);
+       phy_write(phy_data->phydev, 0x00, reg | 0x00800);
+
+       /* Configure SGMII-to-Copper mode */
+       phy_write(phy_data->phydev, 0x1c, 0x7c00);
+       reg = phy_read(phy_data->phydev, 0x1c);
+       reg &= 0x03ff;
+       reg &= ~0x0006;
+       phy_write(phy_data->phydev, 0x1c, 0x8000 | 0x7c00 | reg | 0x0004);
+
+       /* Power up SerDes */
+       reg = phy_read(phy_data->phydev, 0x00);
+       phy_write(phy_data->phydev, 0x00, reg & ~0x00800);
+
+       /* Enable copper register bank */
+       phy_write(phy_data->phydev, 0x1c, 0x7c00);
+       reg = phy_read(phy_data->phydev, 0x1c);
+       reg &= 0x03ff;
+       reg &= ~0x0001;
+       phy_write(phy_data->phydev, 0x1c, 0x8000 | 0x7c00 | reg);
+
+       /* Power up SerDes */
+       reg = phy_read(phy_data->phydev, 0x00);
+       phy_write(phy_data->phydev, 0x00, reg & ~0x00800);
+
+       phy_data->phydev->supported = PHY_GBIT_FEATURES;
+       phy_data->phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+       phy_data->phydev->advertising = phy_data->phydev->supported;
+
+       netif_dbg(pdata, drv, pdata->netdev,
+                 "BelFuse PHY quirk in place\n");
+
+       return true;
+}
+
 static void xgbe_phy_external_phy_quirks(struct xgbe_prv_data *pdata)
 {
+       if (xgbe_phy_belfuse_phy_quirks(pdata))
+               return;
+
        if (xgbe_phy_finisar_phy_quirks(pdata))
                return;
 }
@@ -910,6 +981,9 @@ static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata)
        if (phy_data->phydev)
                return 0;
 
+       /* Clear the extra AN flag */
+       pdata->an_again = 0;
+
        /* Check for the use of an external PHY */
        if (phy_data->phydev_mode == XGBE_MDIO_MODE_NONE)
                return 0;
@@ -1034,37 +1108,6 @@ static bool xgbe_phy_check_sfp_mod_absent(struct xgbe_phy_data *phy_data)
        return false;
 }
 
-static bool xgbe_phy_belfuse_parse_quirks(struct xgbe_prv_data *pdata)
-{
-       struct xgbe_phy_data *phy_data = pdata->phy_data;
-       struct xgbe_sfp_eeprom *sfp_eeprom = &phy_data->sfp_eeprom;
-
-       if (memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_NAME],
-                  XGBE_BEL_FUSE_VENDOR, XGBE_SFP_BASE_VENDOR_NAME_LEN))
-               return false;
-
-       if (!memcmp(&sfp_eeprom->base[XGBE_SFP_BASE_VENDOR_PN],
-                   XGBE_BEL_FUSE_PARTNO, XGBE_SFP_BASE_VENDOR_PN_LEN)) {
-               phy_data->sfp_base = XGBE_SFP_BASE_1000_SX;
-               phy_data->sfp_cable = XGBE_SFP_CABLE_ACTIVE;
-               phy_data->sfp_speed = XGBE_SFP_SPEED_1000;
-               if (phy_data->sfp_changed)
-                       netif_dbg(pdata, drv, pdata->netdev,
-                                 "Bel-Fuse SFP quirk in place\n");
-               return true;
-       }
-
-       return false;
-}
-
-static bool xgbe_phy_sfp_parse_quirks(struct xgbe_prv_data *pdata)
-{
-       if (xgbe_phy_belfuse_parse_quirks(pdata))
-               return true;
-
-       return false;
-}
-
 static void xgbe_phy_sfp_parse_eeprom(struct xgbe_prv_data *pdata)
 {
        struct xgbe_phy_data *phy_data = pdata->phy_data;
@@ -1083,9 +1126,6 @@ static void xgbe_phy_sfp_parse_eeprom(struct xgbe_prv_data *pdata)
        phy_data->sfp_tx_fault = xgbe_phy_check_sfp_tx_fault(phy_data);
        phy_data->sfp_rx_los = xgbe_phy_check_sfp_rx_los(phy_data);
 
-       if (xgbe_phy_sfp_parse_quirks(pdata))
-               return;
-
        /* Assume ACTIVE cable unless told it is PASSIVE */
        if (sfp_base[XGBE_SFP_BASE_CABLE] & XGBE_SFP_BASE_CABLE_PASSIVE) {
                phy_data->sfp_cable = XGBE_SFP_CABLE_PASSIVE;
@@ -1227,13 +1267,6 @@ static int xgbe_phy_sfp_read_eeprom(struct xgbe_prv_data *pdata)
 
                memcpy(&phy_data->sfp_eeprom, &sfp_eeprom, sizeof(sfp_eeprom));
 
-               if (sfp_eeprom.extd[XGBE_SFP_EXTD_SFF_8472]) {
-                       u8 diag_type = sfp_eeprom.extd[XGBE_SFP_EXTD_DIAG];
-
-                       if (!(diag_type & XGBE_SFP_EXTD_DIAG_ADDR_CHANGE))
-                               phy_data->sfp_diags = 1;
-               }
-
                xgbe_phy_free_phy_device(pdata);
        } else {
                phy_data->sfp_changed = 0;
@@ -1283,7 +1316,6 @@ static void xgbe_phy_sfp_reset(struct xgbe_phy_data *phy_data)
        phy_data->sfp_rx_los = 0;
        phy_data->sfp_tx_fault = 0;
        phy_data->sfp_mod_absent = 1;
-       phy_data->sfp_diags = 0;
        phy_data->sfp_base = XGBE_SFP_BASE_UNKNOWN;
        phy_data->sfp_cable = XGBE_SFP_CABLE_UNKNOWN;
        phy_data->sfp_speed = XGBE_SFP_SPEED_UNKNOWN;
@@ -1326,6 +1358,130 @@ put:
        xgbe_phy_put_comm_ownership(pdata);
 }
 
+static int xgbe_phy_module_eeprom(struct xgbe_prv_data *pdata,
+                                 struct ethtool_eeprom *eeprom, u8 *data)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       u8 eeprom_addr, eeprom_data[XGBE_SFP_EEPROM_MAX];
+       struct xgbe_sfp_eeprom *sfp_eeprom;
+       unsigned int i, j, rem;
+       int ret;
+
+       rem = eeprom->len;
+
+       if (!eeprom->len) {
+               ret = -EINVAL;
+               goto done;
+       }
+
+       if ((eeprom->offset + eeprom->len) > XGBE_SFP_EEPROM_MAX) {
+               ret = -EINVAL;
+               goto done;
+       }
+
+       if (phy_data->port_mode != XGBE_PORT_MODE_SFP) {
+               ret = -ENXIO;
+               goto done;
+       }
+
+       if (!netif_running(pdata->netdev)) {
+               ret = -EIO;
+               goto done;
+       }
+
+       if (phy_data->sfp_mod_absent) {
+               ret = -EIO;
+               goto done;
+       }
+
+       ret = xgbe_phy_get_comm_ownership(pdata);
+       if (ret) {
+               ret = -EIO;
+               goto done;
+       }
+
+       ret = xgbe_phy_sfp_get_mux(pdata);
+       if (ret) {
+               netdev_err(pdata->netdev, "I2C error setting SFP MUX\n");
+               ret = -EIO;
+               goto put_own;
+       }
+
+       /* Read the SFP serial ID eeprom */
+       eeprom_addr = 0;
+       ret = xgbe_phy_i2c_read(pdata, XGBE_SFP_SERIAL_ID_ADDRESS,
+                               &eeprom_addr, sizeof(eeprom_addr),
+                               eeprom_data, XGBE_SFP_EEPROM_BASE_LEN);
+       if (ret) {
+               netdev_err(pdata->netdev,
+                          "I2C error reading SFP EEPROM\n");
+               ret = -EIO;
+               goto put_mux;
+       }
+
+       sfp_eeprom = (struct xgbe_sfp_eeprom *)eeprom_data;
+
+       if (XGBE_SFP_DIAGS_SUPPORTED(sfp_eeprom)) {
+               /* Read the SFP diagnostic eeprom */
+               eeprom_addr = 0;
+               ret = xgbe_phy_i2c_read(pdata, XGBE_SFP_DIAG_INFO_ADDRESS,
+                                       &eeprom_addr, sizeof(eeprom_addr),
+                                       eeprom_data + XGBE_SFP_EEPROM_BASE_LEN,
+                                       XGBE_SFP_EEPROM_DIAG_LEN);
+               if (ret) {
+                       netdev_err(pdata->netdev,
+                                  "I2C error reading SFP DIAGS\n");
+                       ret = -EIO;
+                       goto put_mux;
+               }
+       }
+
+       for (i = 0, j = eeprom->offset; i < eeprom->len; i++, j++) {
+               if ((j >= XGBE_SFP_EEPROM_BASE_LEN) &&
+                   !XGBE_SFP_DIAGS_SUPPORTED(sfp_eeprom))
+                       break;
+
+               data[i] = eeprom_data[j];
+               rem--;
+       }
+
+put_mux:
+       xgbe_phy_sfp_put_mux(pdata);
+
+put_own:
+       xgbe_phy_put_comm_ownership(pdata);
+
+done:
+       eeprom->len -= rem;
+
+       return ret;
+}
+
+static int xgbe_phy_module_info(struct xgbe_prv_data *pdata,
+                               struct ethtool_modinfo *modinfo)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       if (phy_data->port_mode != XGBE_PORT_MODE_SFP)
+               return -ENXIO;
+
+       if (!netif_running(pdata->netdev))
+               return -EIO;
+
+       if (phy_data->sfp_mod_absent)
+               return -EIO;
+
+       if (XGBE_SFP_DIAGS_SUPPORTED(&phy_data->sfp_eeprom)) {
+               modinfo->type = ETH_MODULE_SFF_8472;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
+       } else {
+               modinfo->type = ETH_MODULE_SFF_8079;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
+       }
+
+       return 0;
+}
+
 static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata)
 {
        struct ethtool_link_ksettings *lks = &pdata->phy.lks;
@@ -1611,6 +1767,10 @@ static void xgbe_phy_an_advertising(struct xgbe_prv_data *pdata,
        XGBE_CLR_ADV(dlks, 1000baseKX_Full);
        XGBE_CLR_ADV(dlks, 10000baseKR_Full);
 
+       /* Advertise FEC support is present */
+       if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE)
+               XGBE_SET_ADV(dlks, 10000baseR_FEC);
+
        switch (phy_data->port_mode) {
        case XGBE_PORT_MODE_BACKPLANE:
                XGBE_SET_ADV(dlks, 10000baseKR_Full);
@@ -2421,22 +2581,21 @@ static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart)
 static void xgbe_phy_sfp_gpio_setup(struct xgbe_prv_data *pdata)
 {
        struct xgbe_phy_data *phy_data = pdata->phy_data;
-       unsigned int reg;
-
-       reg = XP_IOREAD(pdata, XP_PROP_3);
 
        phy_data->sfp_gpio_address = XGBE_GPIO_ADDRESS_PCA9555 +
-                                    XP_GET_BITS(reg, XP_PROP_3, GPIO_ADDR);
+                                    XP_GET_BITS(pdata->pp3, XP_PROP_3,
+                                                GPIO_ADDR);
 
-       phy_data->sfp_gpio_mask = XP_GET_BITS(reg, XP_PROP_3, GPIO_MASK);
+       phy_data->sfp_gpio_mask = XP_GET_BITS(pdata->pp3, XP_PROP_3,
+                                             GPIO_MASK);
 
-       phy_data->sfp_gpio_rx_los = XP_GET_BITS(reg, XP_PROP_3,
+       phy_data->sfp_gpio_rx_los = XP_GET_BITS(pdata->pp3, XP_PROP_3,
                                                GPIO_RX_LOS);
-       phy_data->sfp_gpio_tx_fault = XP_GET_BITS(reg, XP_PROP_3,
+       phy_data->sfp_gpio_tx_fault = XP_GET_BITS(pdata->pp3, XP_PROP_3,
                                                  GPIO_TX_FAULT);
-       phy_data->sfp_gpio_mod_absent = XP_GET_BITS(reg, XP_PROP_3,
+       phy_data->sfp_gpio_mod_absent = XP_GET_BITS(pdata->pp3, XP_PROP_3,
                                                    GPIO_MOD_ABS);
-       phy_data->sfp_gpio_rate_select = XP_GET_BITS(reg, XP_PROP_3,
+       phy_data->sfp_gpio_rate_select = XP_GET_BITS(pdata->pp3, XP_PROP_3,
                                                     GPIO_RATE_SELECT);
 
        if (netif_msg_probe(pdata)) {
@@ -2458,18 +2617,17 @@ static void xgbe_phy_sfp_gpio_setup(struct xgbe_prv_data *pdata)
 static void xgbe_phy_sfp_comm_setup(struct xgbe_prv_data *pdata)
 {
        struct xgbe_phy_data *phy_data = pdata->phy_data;
-       unsigned int reg, mux_addr_hi, mux_addr_lo;
+       unsigned int mux_addr_hi, mux_addr_lo;
 
-       reg = XP_IOREAD(pdata, XP_PROP_4);
-
-       mux_addr_hi = XP_GET_BITS(reg, XP_PROP_4, MUX_ADDR_HI);
-       mux_addr_lo = XP_GET_BITS(reg, XP_PROP_4, MUX_ADDR_LO);
+       mux_addr_hi = XP_GET_BITS(pdata->pp4, XP_PROP_4, MUX_ADDR_HI);
+       mux_addr_lo = XP_GET_BITS(pdata->pp4, XP_PROP_4, MUX_ADDR_LO);
        if (mux_addr_lo == XGBE_SFP_DIRECT)
                return;
 
        phy_data->sfp_comm = XGBE_SFP_COMM_PCA9545;
        phy_data->sfp_mux_address = (mux_addr_hi << 2) + mux_addr_lo;
-       phy_data->sfp_mux_channel = XP_GET_BITS(reg, XP_PROP_4, MUX_CHAN);
+       phy_data->sfp_mux_channel = XP_GET_BITS(pdata->pp4, XP_PROP_4,
+                                               MUX_CHAN);
 
        if (netif_msg_probe(pdata)) {
                dev_dbg(pdata->dev, "SFP: mux_address=%#x\n",
@@ -2592,13 +2750,11 @@ static bool xgbe_phy_redrv_error(struct xgbe_phy_data *phy_data)
 static int xgbe_phy_mdio_reset_setup(struct xgbe_prv_data *pdata)
 {
        struct xgbe_phy_data *phy_data = pdata->phy_data;
-       unsigned int reg;
 
        if (phy_data->conn_type != XGBE_CONN_TYPE_MDIO)
                return 0;
 
-       reg = XP_IOREAD(pdata, XP_PROP_3);
-       phy_data->mdio_reset = XP_GET_BITS(reg, XP_PROP_3, MDIO_RESET);
+       phy_data->mdio_reset = XP_GET_BITS(pdata->pp3, XP_PROP_3, MDIO_RESET);
        switch (phy_data->mdio_reset) {
        case XGBE_MDIO_RESET_NONE:
        case XGBE_MDIO_RESET_I2C_GPIO:
@@ -2612,12 +2768,12 @@ static int xgbe_phy_mdio_reset_setup(struct xgbe_prv_data *pdata)
 
        if (phy_data->mdio_reset == XGBE_MDIO_RESET_I2C_GPIO) {
                phy_data->mdio_reset_addr = XGBE_GPIO_ADDRESS_PCA9555 +
-                                           XP_GET_BITS(reg, XP_PROP_3,
+                                           XP_GET_BITS(pdata->pp3, XP_PROP_3,
                                                        MDIO_RESET_I2C_ADDR);
-               phy_data->mdio_reset_gpio = XP_GET_BITS(reg, XP_PROP_3,
+               phy_data->mdio_reset_gpio = XP_GET_BITS(pdata->pp3, XP_PROP_3,
                                                        MDIO_RESET_I2C_GPIO);
        } else if (phy_data->mdio_reset == XGBE_MDIO_RESET_INT_GPIO) {
-               phy_data->mdio_reset_gpio = XP_GET_BITS(reg, XP_PROP_3,
+               phy_data->mdio_reset_gpio = XP_GET_BITS(pdata->pp3, XP_PROP_3,
                                                        MDIO_RESET_INT_GPIO);
        }
 
@@ -2707,12 +2863,9 @@ static bool xgbe_phy_conn_type_mismatch(struct xgbe_prv_data *pdata)
 
 static bool xgbe_phy_port_enabled(struct xgbe_prv_data *pdata)
 {
-       unsigned int reg;
-
-       reg = XP_IOREAD(pdata, XP_PROP_0);
-       if (!XP_GET_BITS(reg, XP_PROP_0, PORT_SPEEDS))
+       if (!XP_GET_BITS(pdata->pp0, XP_PROP_0, PORT_SPEEDS))
                return false;
-       if (!XP_GET_BITS(reg, XP_PROP_0, CONN_TYPE))
+       if (!XP_GET_BITS(pdata->pp0, XP_PROP_0, CONN_TYPE))
                return false;
 
        return true;
@@ -2921,7 +3074,6 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
        struct ethtool_link_ksettings *lks = &pdata->phy.lks;
        struct xgbe_phy_data *phy_data;
        struct mii_bus *mii;
-       unsigned int reg;
        int ret;
 
        /* Check if enabled */
@@ -2940,12 +3092,11 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
                return -ENOMEM;
        pdata->phy_data = phy_data;
 
-       reg = XP_IOREAD(pdata, XP_PROP_0);
-       phy_data->port_mode = XP_GET_BITS(reg, XP_PROP_0, PORT_MODE);
-       phy_data->port_id = XP_GET_BITS(reg, XP_PROP_0, PORT_ID);
-       phy_data->port_speeds = XP_GET_BITS(reg, XP_PROP_0, PORT_SPEEDS);
-       phy_data->conn_type = XP_GET_BITS(reg, XP_PROP_0, CONN_TYPE);
-       phy_data->mdio_addr = XP_GET_BITS(reg, XP_PROP_0, MDIO_ADDR);
+       phy_data->port_mode = XP_GET_BITS(pdata->pp0, XP_PROP_0, PORT_MODE);
+       phy_data->port_id = XP_GET_BITS(pdata->pp0, XP_PROP_0, PORT_ID);
+       phy_data->port_speeds = XP_GET_BITS(pdata->pp0, XP_PROP_0, PORT_SPEEDS);
+       phy_data->conn_type = XP_GET_BITS(pdata->pp0, XP_PROP_0, CONN_TYPE);
+       phy_data->mdio_addr = XP_GET_BITS(pdata->pp0, XP_PROP_0, MDIO_ADDR);
        if (netif_msg_probe(pdata)) {
                dev_dbg(pdata->dev, "port mode=%u\n", phy_data->port_mode);
                dev_dbg(pdata->dev, "port id=%u\n", phy_data->port_id);
@@ -2954,12 +3105,11 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
                dev_dbg(pdata->dev, "mdio addr=%u\n", phy_data->mdio_addr);
        }
 
-       reg = XP_IOREAD(pdata, XP_PROP_4);
-       phy_data->redrv = XP_GET_BITS(reg, XP_PROP_4, REDRV_PRESENT);
-       phy_data->redrv_if = XP_GET_BITS(reg, XP_PROP_4, REDRV_IF);
-       phy_data->redrv_addr = XP_GET_BITS(reg, XP_PROP_4, REDRV_ADDR);
-       phy_data->redrv_lane = XP_GET_BITS(reg, XP_PROP_4, REDRV_LANE);
-       phy_data->redrv_model = XP_GET_BITS(reg, XP_PROP_4, REDRV_MODEL);
+       phy_data->redrv = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_PRESENT);
+       phy_data->redrv_if = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_IF);
+       phy_data->redrv_addr = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_ADDR);
+       phy_data->redrv_lane = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_LANE);
+       phy_data->redrv_model = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_MODEL);
        if (phy_data->redrv && netif_msg_probe(pdata)) {
                dev_dbg(pdata->dev, "redrv present\n");
                dev_dbg(pdata->dev, "redrv i/f=%u\n", phy_data->redrv_if);
@@ -3231,4 +3381,7 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if)
 
        phy_impl->kr_training_pre       = xgbe_phy_kr_training_pre;
        phy_impl->kr_training_post      = xgbe_phy_kr_training_post;
+
+       phy_impl->module_info           = xgbe_phy_module_info;
+       phy_impl->module_eeprom         = xgbe_phy_module_eeprom;
 }
index 95d4b56..47bcbcf 100644 (file)
 #define XGBE_TX_DESC_MAX_PROC  (XGBE_TX_DESC_CNT >> 1)
 #define XGBE_RX_DESC_CNT       512
 
+#define XGBE_TX_DESC_CNT_MIN   64
+#define XGBE_TX_DESC_CNT_MAX   4096
+#define XGBE_RX_DESC_CNT_MIN   64
+#define XGBE_RX_DESC_CNT_MAX   4096
+
 #define XGBE_TX_MAX_BUF_SIZE   (0x3fff & ~(64 - 1))
 
 /* Descriptors required for maximum contiguous TSO/GSO packet */
@@ -835,6 +840,7 @@ struct xgbe_hw_if {
  *   Optional routines:
  *     an_pre, an_post
  *     kr_training_pre, kr_training_post
+ *     module_info, module_eeprom
  */
 struct xgbe_phy_impl_if {
        /* Perform Setup/teardown actions */
@@ -883,6 +889,12 @@ struct xgbe_phy_impl_if {
        /* Pre/Post KR training enablement support */
        void (*kr_training_pre)(struct xgbe_prv_data *);
        void (*kr_training_post)(struct xgbe_prv_data *);
+
+       /* SFP module related info */
+       int (*module_info)(struct xgbe_prv_data *pdata,
+                          struct ethtool_modinfo *modinfo);
+       int (*module_eeprom)(struct xgbe_prv_data *pdata,
+                            struct ethtool_eeprom *eeprom, u8 *data);
 };
 
 struct xgbe_phy_if {
@@ -905,6 +917,12 @@ struct xgbe_phy_if {
        /* For single interrupt support */
        irqreturn_t (*an_isr)(struct xgbe_prv_data *);
 
+       /* For ethtool PHY support */
+       int (*module_info)(struct xgbe_prv_data *pdata,
+                          struct ethtool_modinfo *modinfo);
+       int (*module_eeprom)(struct xgbe_prv_data *pdata,
+                            struct ethtool_eeprom *eeprom, u8 *data);
+
        /* PHY implementation specific services */
        struct xgbe_phy_impl_if phy_impl;
 };
@@ -1027,6 +1045,13 @@ struct xgbe_prv_data {
        void __iomem *xprop_regs;       /* XGBE property registers */
        void __iomem *xi2c_regs;        /* XGBE I2C CSRs */
 
+       /* Port property registers */
+       unsigned int pp0;
+       unsigned int pp1;
+       unsigned int pp2;
+       unsigned int pp3;
+       unsigned int pp4;
+
        /* Overall device lock */
        spinlock_t lock;
 
@@ -1097,6 +1122,9 @@ struct xgbe_prv_data {
        unsigned int rx_ring_count;
        unsigned int rx_desc_count;
 
+       unsigned int new_tx_ring_count;
+       unsigned int new_rx_ring_count;
+
        unsigned int tx_max_q_count;
        unsigned int rx_max_q_count;
        unsigned int tx_q_count;
@@ -1233,6 +1261,7 @@ struct xgbe_prv_data {
        enum xgbe_rx kr_state;
        enum xgbe_rx kx_state;
        struct work_struct an_work;
+       unsigned int an_again;
        unsigned int an_supported;
        unsigned int parallel_detect;
        unsigned int fec_ability;
@@ -1310,6 +1339,8 @@ int xgbe_powerup(struct net_device *, unsigned int);
 int xgbe_powerdown(struct net_device *, unsigned int);
 void xgbe_init_rx_coalesce(struct xgbe_prv_data *);
 void xgbe_init_tx_coalesce(struct xgbe_prv_data *);
+void xgbe_restart_dev(struct xgbe_prv_data *pdata);
+void xgbe_full_restart_dev(struct xgbe_prv_data *pdata);
 
 #ifdef CONFIG_DEBUG_FS
 void xgbe_debugfs_init(struct xgbe_prv_data *);
index b4c9268..3e93df5 100644 (file)
@@ -591,16 +591,10 @@ static int macb_mii_init(struct macb *bp)
        dev_set_drvdata(&bp->dev->dev, bp->mii_bus);
 
        np = bp->pdev->dev.of_node;
+       if (pdata)
+               bp->mii_bus->phy_mask = pdata->phy_mask;
 
-       if (np) {
-               err = of_mdiobus_register(bp->mii_bus, np);
-       } else {
-               if (pdata)
-                       bp->mii_bus->phy_mask = pdata->phy_mask;
-
-               err = mdiobus_register(bp->mii_bus);
-       }
-
+       err = of_mdiobus_register(bp->mii_bus, np);
        if (err)
                goto err_out_free_mdiobus;
 
index b57acb8..3c50578 100644 (file)
@@ -62,6 +62,18 @@ struct cudbg_hw_sched {
        u32 map;
 };
 
+#define SGE_QBASE_DATA_REG_NUM 4
+
+struct sge_qbase_reg_field {
+       u32 reg_addr;
+       u32 reg_data[SGE_QBASE_DATA_REG_NUM];
+       /* Max supported PFs */
+       u32 pf_data_value[PCIE_FW_MASTER_M + 1][SGE_QBASE_DATA_REG_NUM];
+       /* Max supported VFs */
+       u32 vf_data_value[T6_VF_M + 1][SGE_QBASE_DATA_REG_NUM];
+       u32 vfcount; /* Actual number of max vfs in current configuration */
+};
+
 struct ireg_field {
        u32 ireg_addr;
        u32 ireg_data;
@@ -235,6 +247,9 @@ struct cudbg_vpd_data {
 };
 
 #define CUDBG_MAX_TCAM_TID 0x800
+#define CUDBG_T6_CLIP 1536
+#define CUDBG_MAX_TID_COMP_EN 6144
+#define CUDBG_MAX_TID_COMP_DIS 3072
 
 enum cudbg_le_entry_types {
        LE_ET_UNKNOWN = 0,
@@ -354,6 +369,11 @@ static const u32 t5_sge_dbg_index_array[2][IREG_NUM_ELEM] = {
        {0x10cc, 0x10d4, 0x0, 16},
 };
 
+static const u32 t6_sge_qbase_index_array[] = {
+       /* 1 addr reg SGE_QBASE_INDEX and 4 data reg SGE_QBASE_MAP[0-3] */
+       0x1250, 0x1240, 0x1244, 0x1248, 0x124c,
+};
+
 static const u32 t5_pcie_pdbg_array[][IREG_NUM_ELEM] = {
        {0x5a04, 0x5a0c, 0x00, 0x20}, /* t5_pcie_pdbg_regs_00_to_20 */
        {0x5a04, 0x5a0c, 0x21, 0x20}, /* t5_pcie_pdbg_regs_21_to_40 */
@@ -419,15 +439,15 @@ static const u32 t6_up_cim_reg_array[][IREG_NUM_ELEM + 1] = {
        {0x7b50, 0x7b54, 0x280, 0x20, 0}, /* up_cim_280_to_2fc */
        {0x7b50, 0x7b54, 0x300, 0x20, 0}, /* up_cim_300_to_37c */
        {0x7b50, 0x7b54, 0x380, 0x14, 0}, /* up_cim_380_to_3cc */
-       {0x7b50, 0x7b54, 0x2900, 0x4, 0x4}, /* up_cim_2900_to_3d40 */
-       {0x7b50, 0x7b54, 0x2904, 0x4, 0x4}, /* up_cim_2904_to_3d44 */
-       {0x7b50, 0x7b54, 0x2908, 0x4, 0x4}, /* up_cim_2908_to_3d48 */
-       {0x7b50, 0x7b54, 0x2910, 0x4, 0x4}, /* up_cim_2910_to_3d4c */
-       {0x7b50, 0x7b54, 0x2914, 0x4, 0x4}, /* up_cim_2914_to_3d50 */
-       {0x7b50, 0x7b54, 0x2920, 0x10, 0x10}, /* up_cim_2920_to_2a10 */
-       {0x7b50, 0x7b54, 0x2924, 0x10, 0x10}, /* up_cim_2924_to_2a14 */
-       {0x7b50, 0x7b54, 0x2928, 0x10, 0x10}, /* up_cim_2928_to_2a18 */
-       {0x7b50, 0x7b54, 0x292c, 0x10, 0x10}, /* up_cim_292c_to_2a1c */
+       {0x7b50, 0x7b54, 0x4900, 0x4, 0x4}, /* up_cim_4900_to_4c60 */
+       {0x7b50, 0x7b54, 0x4904, 0x4, 0x4}, /* up_cim_4904_to_4c64 */
+       {0x7b50, 0x7b54, 0x4908, 0x4, 0x4}, /* up_cim_4908_to_4c68 */
+       {0x7b50, 0x7b54, 0x4910, 0x4, 0x4}, /* up_cim_4910_to_4c70 */
+       {0x7b50, 0x7b54, 0x4914, 0x4, 0x4}, /* up_cim_4914_to_4c74 */
+       {0x7b50, 0x7b54, 0x4920, 0x10, 0x10}, /* up_cim_4920_to_4a10 */
+       {0x7b50, 0x7b54, 0x4924, 0x10, 0x10}, /* up_cim_4924_to_4a14 */
+       {0x7b50, 0x7b54, 0x4928, 0x10, 0x10}, /* up_cim_4928_to_4a18 */
+       {0x7b50, 0x7b54, 0x492c, 0x10, 0x10}, /* up_cim_492c_to_4a1c */
 };
 
 static const u32 t5_up_cim_reg_array[][IREG_NUM_ELEM + 1] = {
@@ -444,16 +464,6 @@ static const u32 t5_up_cim_reg_array[][IREG_NUM_ELEM + 1] = {
        {0x7b50, 0x7b54, 0x280, 0x20, 0}, /* up_cim_280_to_2fc */
        {0x7b50, 0x7b54, 0x300, 0x20, 0}, /* up_cim_300_to_37c */
        {0x7b50, 0x7b54, 0x380, 0x14, 0}, /* up_cim_380_to_3cc */
-       {0x7b50, 0x7b54, 0x2900, 0x4, 0x4}, /* up_cim_2900_to_3d40 */
-       {0x7b50, 0x7b54, 0x2904, 0x4, 0x4}, /* up_cim_2904_to_3d44 */
-       {0x7b50, 0x7b54, 0x2908, 0x4, 0x4}, /* up_cim_2908_to_3d48 */
-       {0x7b50, 0x7b54, 0x2910, 0x4, 0x4}, /* up_cim_2910_to_3d4c */
-       {0x7b50, 0x7b54, 0x2914, 0x4, 0x4}, /* up_cim_2914_to_3d50 */
-       {0x7b50, 0x7b54, 0x2918, 0x4, 0x4}, /* up_cim_2918_to_3d54 */
-       {0x7b50, 0x7b54, 0x291c, 0x4, 0x4}, /* up_cim_291c_to_3d58 */
-       {0x7b50, 0x7b54, 0x2924, 0x10, 0x10}, /* up_cim_2924_to_2914 */
-       {0x7b50, 0x7b54, 0x2928, 0x10, 0x10}, /* up_cim_2928_to_2a18 */
-       {0x7b50, 0x7b54, 0x292c, 0x10, 0x10}, /* up_cim_292c_to_2a1c */
 };
 
 static const u32 t6_hma_ireg_array[][IREG_NUM_ELEM] = {
index 8568a51..215fe62 100644 (file)
@@ -24,6 +24,7 @@
 #define CUDBG_STATUS_NOT_IMPLEMENTED -28
 #define CUDBG_SYSTEM_ERROR -29
 #define CUDBG_STATUS_CCLK_NOT_DEFINED -32
+#define CUDBG_STATUS_PARTIAL_DATA -41
 
 #define CUDBG_MAJOR_VERSION 1
 #define CUDBG_MINOR_VERSION 14
index 9da6f57..0afcfe9 100644 (file)
@@ -1339,16 +1339,39 @@ int cudbg_collect_tp_indirect(struct cudbg_init *pdbg_init,
        return cudbg_write_and_release_buff(pdbg_init, &temp_buff, dbg_buff);
 }
 
+static void cudbg_read_sge_qbase_indirect_reg(struct adapter *padap,
+                                             struct sge_qbase_reg_field *qbase,
+                                             u32 func, bool is_pf)
+{
+       u32 *buff, i;
+
+       if (is_pf) {
+               buff = qbase->pf_data_value[func];
+       } else {
+               buff = qbase->vf_data_value[func];
+               /* In SGE_QBASE_INDEX,
+                * Entries 0->7 are PF0->7, Entries 8->263 are VFID0->256.
+                */
+               func += 8;
+       }
+
+       t4_write_reg(padap, qbase->reg_addr, func);
+       for (i = 0; i < SGE_QBASE_DATA_REG_NUM; i++, buff++)
+               *buff = t4_read_reg(padap, qbase->reg_data[i]);
+}
+
 int cudbg_collect_sge_indirect(struct cudbg_init *pdbg_init,
                               struct cudbg_buffer *dbg_buff,
                               struct cudbg_error *cudbg_err)
 {
        struct adapter *padap = pdbg_init->adap;
        struct cudbg_buffer temp_buff = { 0 };
+       struct sge_qbase_reg_field *sge_qbase;
        struct ireg_buf *ch_sge_dbg;
        int i, rc;
 
-       rc = cudbg_get_buff(pdbg_init, dbg_buff, sizeof(*ch_sge_dbg) * 2,
+       rc = cudbg_get_buff(pdbg_init, dbg_buff,
+                           sizeof(*ch_sge_dbg) * 2 + sizeof(*sge_qbase),
                            &temp_buff);
        if (rc)
                return rc;
@@ -1370,6 +1393,28 @@ int cudbg_collect_sge_indirect(struct cudbg_init *pdbg_init,
                                 sge_pio->ireg_local_offset);
                ch_sge_dbg++;
        }
+
+       if (CHELSIO_CHIP_VERSION(padap->params.chip) > CHELSIO_T5) {
+               sge_qbase = (struct sge_qbase_reg_field *)ch_sge_dbg;
+               /* 1 addr reg SGE_QBASE_INDEX and 4 data reg
+                * SGE_QBASE_MAP[0-3]
+                */
+               sge_qbase->reg_addr = t6_sge_qbase_index_array[0];
+               for (i = 0; i < SGE_QBASE_DATA_REG_NUM; i++)
+                       sge_qbase->reg_data[i] =
+                               t6_sge_qbase_index_array[i + 1];
+
+               for (i = 0; i <= PCIE_FW_MASTER_M; i++)
+                       cudbg_read_sge_qbase_indirect_reg(padap, sge_qbase,
+                                                         i, true);
+
+               for (i = 0; i < padap->params.arch.vfcount; i++)
+                       cudbg_read_sge_qbase_indirect_reg(padap, sge_qbase,
+                                                         i, false);
+
+               sge_qbase->vfcount = padap->params.arch.vfcount;
+       }
+
        return cudbg_write_and_release_buff(pdbg_init, &temp_buff, dbg_buff);
 }
 
@@ -2366,8 +2411,11 @@ void cudbg_fill_le_tcam_info(struct adapter *padap,
        value = t4_read_reg(padap, LE_DB_ROUTING_TABLE_INDEX_A);
        tcam_region->routing_start = value;
 
-       /*Get clip table index */
-       value = t4_read_reg(padap, LE_DB_CLIP_TABLE_INDEX_A);
+       /* Get clip table index. For T6 there is separate CLIP TCAM */
+       if (is_t6(padap->params.chip))
+               value = t4_read_reg(padap, LE_DB_CLCAM_TID_BASE_A);
+       else
+               value = t4_read_reg(padap, LE_DB_CLIP_TABLE_INDEX_A);
        tcam_region->clip_start = value;
 
        /* Get filter table index */
@@ -2392,8 +2440,16 @@ void cudbg_fill_le_tcam_info(struct adapter *padap,
                                               tcam_region->tid_hash_base;
                }
        } else { /* hash not enabled */
-               tcam_region->max_tid = CUDBG_MAX_TCAM_TID;
+               if (is_t6(padap->params.chip))
+                       tcam_region->max_tid = (value & ASLIPCOMPEN_F) ?
+                                              CUDBG_MAX_TID_COMP_EN :
+                                              CUDBG_MAX_TID_COMP_DIS;
+               else
+                       tcam_region->max_tid = CUDBG_MAX_TCAM_TID;
        }
+
+       if (is_t6(padap->params.chip))
+               tcam_region->max_tid += CUDBG_T6_CLIP;
 }
 
 int cudbg_collect_le_tcam(struct cudbg_init *pdbg_init,
@@ -2423,18 +2479,31 @@ int cudbg_collect_le_tcam(struct cudbg_init *pdbg_init,
        for (i = 0; i < tcam_region.max_tid; ) {
                rc = cudbg_read_tid(pdbg_init, i, tid_data);
                if (rc) {
-                       cudbg_err->sys_err = rc;
-                       cudbg_put_buff(pdbg_init, &temp_buff);
-                       return rc;
+                       cudbg_err->sys_warn = CUDBG_STATUS_PARTIAL_DATA;
+                       /* Update tcam header and exit */
+                       tcam_region.max_tid = i;
+                       memcpy(temp_buff.data, &tcam_region,
+                              sizeof(struct cudbg_tcam));
+                       goto out;
                }
 
-               /* ipv6 takes two tids */
-               cudbg_is_ipv6_entry(tid_data, tcam_region) ? i += 2 : i++;
+               if (cudbg_is_ipv6_entry(tid_data, tcam_region)) {
+                       /* T6 CLIP TCAM: ipv6 takes 4 entries */
+                       if (is_t6(padap->params.chip) &&
+                           i >= tcam_region.clip_start &&
+                           i < tcam_region.clip_start + CUDBG_T6_CLIP)
+                               i += 4;
+                       else /* Main TCAM: ipv6 takes two tids */
+                               i += 2;
+               } else {
+                       i++;
+               }
 
                tid_data++;
                bytes += sizeof(struct cudbg_tid_data);
        }
 
+out:
        return cudbg_write_and_release_buff(pdbg_init, &temp_buff, dbg_buff);
 }
 
index 688f954..0f305d9 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/net_tstamp.h>
 #include <linux/ptp_clock_kernel.h>
 #include <linux/ptp_classify.h>
+#include <linux/crash_dump.h>
 #include <asm/io.h>
 #include "t4_chip_type.h"
 #include "cxgb4_uld.h"
@@ -490,6 +491,9 @@ struct link_config {
 
        unsigned char  link_ok;          /* link up? */
        unsigned char  link_down_rc;     /* link down reason */
+
+       bool new_module;                 /* ->OS Transceiver Module inserted */
+       bool redo_l1cfg;                 /* ->CC redo current "sticky" L1 CFG */
 };
 
 #define FW_LEN16(fw_struct) FW_CMD_LEN16_V(sizeof(fw_struct) / 16)
@@ -964,6 +968,9 @@ struct adapter {
        struct hma_data hma;
 
        struct srq_data *srq;
+
+       /* Dump buffer for collecting logs in kdump kernel */
+       struct vmcoredd_data vmcoredd;
 };
 
 /* Support for "sched-class" command to allow a TX Scheduling Class to be
@@ -1034,6 +1041,7 @@ struct ch_sched_queue {
 #define VF_BITWIDTH 8
 #define IVLAN_BITWIDTH 16
 #define OVLAN_BITWIDTH 16
+#define ENCAP_VNI_BITWIDTH 24
 
 /* Filter matching rules.  These consist of a set of ingress packet field
  * (value, mask) tuples.  The associated ingress packet field matches the
@@ -1064,6 +1072,7 @@ struct ch_filter_tuple {
        uint32_t ivlan_vld:1;                   /* inner VLAN valid */
        uint32_t ovlan_vld:1;                   /* outer VLAN valid */
        uint32_t pfvf_vld:1;                    /* PF/VF valid */
+       uint32_t encap_vld:1;                   /* Encapsulation valid */
        uint32_t macidx:MACIDX_BITWIDTH;        /* exact match MAC index */
        uint32_t fcoe:FCOE_BITWIDTH;            /* FCoE packet */
        uint32_t iport:IPORT_BITWIDTH;          /* ingress port */
@@ -1074,6 +1083,7 @@ struct ch_filter_tuple {
        uint32_t vf:VF_BITWIDTH;                /* PCI-E VF ID */
        uint32_t ivlan:IVLAN_BITWIDTH;          /* inner VLAN */
        uint32_t ovlan:OVLAN_BITWIDTH;          /* outer VLAN */
+       uint32_t vni:ENCAP_VNI_BITWIDTH;        /* VNI of tunnel */
 
        /* Uncompressed header matching field rules.  These are always
         * available for field rules.
@@ -1317,7 +1327,7 @@ static inline unsigned int qtimer_val(const struct adapter *adap,
 extern char cxgb4_driver_name[];
 extern const char cxgb4_driver_version[];
 
-void t4_os_portmod_changed(const struct adapter *adap, int port_id);
+void t4_os_portmod_changed(struct adapter *adap, int port_id);
 void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat);
 
 void t4_free_sge_resources(struct adapter *adap);
@@ -1498,8 +1508,25 @@ void t4_intr_disable(struct adapter *adapter);
 int t4_slow_intr_handler(struct adapter *adapter);
 
 int t4_wait_dev_ready(void __iomem *regs);
-int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port,
-                 struct link_config *lc);
+
+int t4_link_l1cfg_core(struct adapter *adap, unsigned int mbox,
+                      unsigned int port, struct link_config *lc,
+                      bool sleep_ok, int timeout);
+
+static inline int t4_link_l1cfg(struct adapter *adapter, unsigned int mbox,
+                               unsigned int port, struct link_config *lc)
+{
+       return t4_link_l1cfg_core(adapter, mbox, port, lc,
+                                 true, FW_CMD_MAX_TIMEOUT);
+}
+
+static inline int t4_link_l1cfg_ns(struct adapter *adapter, unsigned int mbox,
+                                  unsigned int port, struct link_config *lc)
+{
+       return t4_link_l1cfg_core(adapter, mbox, port, lc,
+                                 false, FW_CMD_MAX_TIMEOUT);
+}
+
 int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port);
 
 u32 t4_read_pcie_cfg4(struct adapter *adap, int reg);
@@ -1690,6 +1717,12 @@ int t4_set_rxmode(struct adapter *adap, unsigned int mbox, unsigned int viid,
 int t4_free_raw_mac_filt(struct adapter *adap, unsigned int viid,
                         const u8 *addr, const u8 *mask, unsigned int idx,
                         u8 lookup_type, u8 port_id, bool sleep_ok);
+int t4_free_encap_mac_filt(struct adapter *adap, unsigned int viid, int idx,
+                          bool sleep_ok);
+int t4_alloc_encap_mac_filt(struct adapter *adap, unsigned int viid,
+                           const u8 *addr, const u8 *mask, unsigned int vni,
+                           unsigned int vni_mask, u8 dip_hit, u8 lookup_type,
+                           bool sleep_ok);
 int t4_alloc_raw_mac_filt(struct adapter *adap, unsigned int viid,
                          const u8 *addr, const u8 *mask, unsigned int idx,
                          u8 lookup_type, u8 port_id, bool sleep_ok);
index 143686c..8d751ef 100644 (file)
@@ -214,7 +214,8 @@ static u32 cxgb4_get_entity_length(struct adapter *adap, u32 entity)
                len = sizeof(struct ireg_buf) * n;
                break;
        case CUDBG_SGE_INDIRECT:
-               len = sizeof(struct ireg_buf) * 2;
+               len = sizeof(struct ireg_buf) * 2 +
+                     sizeof(struct sge_qbase_reg_field);
                break;
        case CUDBG_ULPRX_LA:
                len = sizeof(struct cudbg_ulprx_la);
@@ -488,3 +489,28 @@ void cxgb4_init_ethtool_dump(struct adapter *adapter)
        adapter->eth_dump.version = adapter->params.fw_vers;
        adapter->eth_dump.len = 0;
 }
+
+static int cxgb4_cudbg_vmcoredd_collect(struct vmcoredd_data *data, void *buf)
+{
+       struct adapter *adap = container_of(data, struct adapter, vmcoredd);
+       u32 len = data->size;
+
+       return cxgb4_cudbg_collect(adap, buf, &len, CXGB4_ETH_DUMP_ALL);
+}
+
+int cxgb4_cudbg_vmcore_add_dump(struct adapter *adap)
+{
+       struct vmcoredd_data *data = &adap->vmcoredd;
+       u32 len;
+
+       len = sizeof(struct cudbg_hdr) +
+             sizeof(struct cudbg_entity_hdr) * CUDBG_MAX_ENTITY;
+       len += CUDBG_DUMP_BUFF_SIZE;
+
+       data->size = len;
+       snprintf(data->dump_name, sizeof(data->dump_name), "%s_%s",
+                cxgb4_driver_name, adap->name);
+       data->vmcoredd_callback = cxgb4_cudbg_vmcoredd_collect;
+
+       return vmcore_add_device_dump(data);
+}
index ce1ac9a..ef59ba1 100644 (file)
@@ -41,8 +41,11 @@ enum CXGB4_ETHTOOL_DUMP_FLAGS {
        CXGB4_ETH_DUMP_HW = (1 << 1), /* various FW and HW dumps */
 };
 
+#define CXGB4_ETH_DUMP_ALL (CXGB4_ETH_DUMP_MEM | CXGB4_ETH_DUMP_HW)
+
 u32 cxgb4_get_dump_length(struct adapter *adap, u32 flag);
 int cxgb4_cudbg_collect(struct adapter *adap, void *buf, u32 *buf_size,
                        u32 flag);
 void cxgb4_init_ethtool_dump(struct adapter *adapter);
+int cxgb4_cudbg_vmcore_add_dump(struct adapter *adap);
 #endif /* __CXGB4_CUDBG_H__ */
index aae9802..00fc5f1 100644 (file)
@@ -265,6 +265,8 @@ static int validate_filter(struct net_device *dev,
                        fs->mask.pfvf_vld) ||
            unsupported(fconf, VNIC_ID_F, fs->val.ovlan_vld,
                        fs->mask.ovlan_vld) ||
+           unsupported(fconf, VNIC_ID_F, fs->val.encap_vld,
+                       fs->mask.encap_vld) ||
            unsupported(fconf, VLAN_F, fs->val.ivlan_vld, fs->mask.ivlan_vld))
                return -EOPNOTSUPP;
 
@@ -275,8 +277,12 @@ static int validate_filter(struct net_device *dev,
         * carries that overlap, we need to translate any PF/VF
         * specification into that internal format below.
         */
-       if (is_field_set(fs->val.pfvf_vld, fs->mask.pfvf_vld) &&
-           is_field_set(fs->val.ovlan_vld, fs->mask.ovlan_vld))
+       if ((is_field_set(fs->val.pfvf_vld, fs->mask.pfvf_vld) &&
+            is_field_set(fs->val.ovlan_vld, fs->mask.ovlan_vld)) ||
+           (is_field_set(fs->val.pfvf_vld, fs->mask.pfvf_vld) &&
+            is_field_set(fs->val.encap_vld, fs->mask.encap_vld)) ||
+           (is_field_set(fs->val.ovlan_vld, fs->mask.ovlan_vld) &&
+            is_field_set(fs->val.encap_vld, fs->mask.encap_vld)))
                return -EOPNOTSUPP;
        if (unsupported(iconf, VNIC_F, fs->val.pfvf_vld, fs->mask.pfvf_vld) ||
            (is_field_set(fs->val.ovlan_vld, fs->mask.ovlan_vld) &&
@@ -306,6 +312,9 @@ static int validate_filter(struct net_device *dev,
             fs->newvlan == VLAN_REWRITE))
                return -EOPNOTSUPP;
 
+       if (fs->val.encap_vld &&
+           CHELSIO_CHIP_VERSION(adapter->params.chip) < CHELSIO_T6)
+               return -EOPNOTSUPP;
        return 0;
 }
 
@@ -705,6 +714,8 @@ int delete_filter(struct adapter *adapter, unsigned int fidx)
  */
 void clear_filter(struct adapter *adap, struct filter_entry *f)
 {
+       struct port_info *pi = netdev_priv(f->dev);
+
        /* If the new or old filter have loopback rewriteing rules then we'll
         * need to free any existing L2T, SMT, CLIP entries of filter
         * rule.
@@ -715,6 +726,12 @@ void clear_filter(struct adapter *adap, struct filter_entry *f)
        if (f->smt)
                cxgb4_smt_release(f->smt);
 
+       if (f->fs.val.encap_vld && f->fs.val.ovlan_vld)
+               if (atomic_dec_and_test(&adap->mps_encap[f->fs.val.ovlan &
+                                                        0x1ff].refcnt))
+                       t4_free_encap_mac_filt(adap, pi->viid,
+                                              f->fs.val.ovlan & 0x1ff, 0);
+
        if ((f->fs.hash || is_t6(adap->params.chip)) && f->fs.type)
                cxgb4_clip_release(f->dev, (const u32 *)&f->fs.val.lip, 1);
 
@@ -835,11 +852,15 @@ bool is_filter_exact_match(struct adapter *adap,
 {
        struct tp_params *tp = &adap->params.tp;
        u64 hash_filter_mask = tp->hash_filter_mask;
-       u32 mask;
+       u64 ntuple_mask = 0;
 
        if (!is_hashfilter(adap))
                return false;
 
+        /* Keep tunnel VNI match disabled for hash-filters for now */
+       if (fs->mask.encap_vld)
+               return false;
+
        if (fs->type) {
                if (is_inaddr_any(fs->val.fip, AF_INET6) ||
                    !is_addr_all_mask(fs->mask.fip, AF_INET6))
@@ -864,73 +885,45 @@ bool is_filter_exact_match(struct adapter *adap,
        if (!fs->val.fport || fs->mask.fport != 0xffff)
                return false;
 
-       if (tp->fcoe_shift >= 0) {
-               mask = (hash_filter_mask >> tp->fcoe_shift) & FT_FCOE_W;
-               if (mask && !fs->mask.fcoe)
-                       return false;
-       }
+       /* calculate tuple mask and compare with mask configured in hw */
+       if (tp->fcoe_shift >= 0)
+               ntuple_mask |= (u64)fs->mask.fcoe << tp->fcoe_shift;
 
-       if (tp->port_shift >= 0) {
-               mask = (hash_filter_mask >> tp->port_shift) & FT_PORT_W;
-               if (mask && !fs->mask.iport)
-                       return false;
-       }
+       if (tp->port_shift >= 0)
+               ntuple_mask |= (u64)fs->mask.iport << tp->port_shift;
 
        if (tp->vnic_shift >= 0) {
-               mask = (hash_filter_mask >> tp->vnic_shift) & FT_VNIC_ID_W;
-
-               if ((adap->params.tp.ingress_config & VNIC_F)) {
-                       if (mask && !fs->mask.pfvf_vld)
-                               return false;
-               } else {
-                       if (mask && !fs->mask.ovlan_vld)
-                               return false;
-               }
+               if ((adap->params.tp.ingress_config & VNIC_F))
+                       ntuple_mask |= (u64)fs->mask.pfvf_vld << tp->vnic_shift;
+               else
+                       ntuple_mask |= (u64)fs->mask.ovlan_vld <<
+                               tp->vnic_shift;
        }
 
-       if (tp->vlan_shift >= 0) {
-               mask = (hash_filter_mask >> tp->vlan_shift) & FT_VLAN_W;
-               if (mask && !fs->mask.ivlan)
-                       return false;
-       }
+       if (tp->vlan_shift >= 0)
+               ntuple_mask |= (u64)fs->mask.ivlan << tp->vlan_shift;
 
-       if (tp->tos_shift >= 0) {
-               mask = (hash_filter_mask >> tp->tos_shift) & FT_TOS_W;
-               if (mask && !fs->mask.tos)
-                       return false;
-       }
+       if (tp->tos_shift >= 0)
+               ntuple_mask |= (u64)fs->mask.tos << tp->tos_shift;
 
-       if (tp->protocol_shift >= 0) {
-               mask = (hash_filter_mask >> tp->protocol_shift) & FT_PROTOCOL_W;
-               if (mask && !fs->mask.proto)
-                       return false;
-       }
+       if (tp->protocol_shift >= 0)
+               ntuple_mask |= (u64)fs->mask.proto << tp->protocol_shift;
 
-       if (tp->ethertype_shift >= 0) {
-               mask = (hash_filter_mask >> tp->ethertype_shift) &
-                       FT_ETHERTYPE_W;
-               if (mask && !fs->mask.ethtype)
-                       return false;
-       }
+       if (tp->ethertype_shift >= 0)
+               ntuple_mask |= (u64)fs->mask.ethtype << tp->ethertype_shift;
 
-       if (tp->macmatch_shift >= 0) {
-               mask = (hash_filter_mask >> tp->macmatch_shift) & FT_MACMATCH_W;
-               if (mask && !fs->mask.macidx)
-                       return false;
-       }
+       if (tp->macmatch_shift >= 0)
+               ntuple_mask |= (u64)fs->mask.macidx << tp->macmatch_shift;
+
+       if (tp->matchtype_shift >= 0)
+               ntuple_mask |= (u64)fs->mask.matchtype << tp->matchtype_shift;
+
+       if (tp->frag_shift >= 0)
+               ntuple_mask |= (u64)fs->mask.frag << tp->frag_shift;
+
+       if (ntuple_mask != hash_filter_mask)
+               return false;
 
-       if (tp->matchtype_shift >= 0) {
-               mask = (hash_filter_mask >> tp->matchtype_shift) &
-                       FT_MPSHITTYPE_W;
-               if (mask && !fs->mask.matchtype)
-                       return false;
-       }
-       if (tp->frag_shift >= 0) {
-               mask = (hash_filter_mask >> tp->frag_shift) &
-                       FT_FRAGMENTATION_W;
-               if (mask && !fs->mask.frag)
-                       return false;
-       }
        return true;
 }
 
@@ -961,8 +954,12 @@ static u64 hash_filter_ntuple(struct ch_filter_specification *fs,
                ntuple |= (u64)(fs->val.tos) << tp->tos_shift;
 
        if (tp->vnic_shift >= 0) {
-               if ((adap->params.tp.ingress_config & VNIC_F) &&
-                   fs->mask.pfvf_vld)
+               if ((adap->params.tp.ingress_config & USE_ENC_IDX_F) &&
+                   fs->mask.encap_vld)
+                       ntuple |= (u64)((fs->val.encap_vld << 16) |
+                                       (fs->val.ovlan)) << tp->vnic_shift;
+               else if ((adap->params.tp.ingress_config & VNIC_F) &&
+                        fs->mask.pfvf_vld)
                        ntuple |= (u64)((fs->val.pfvf_vld << 16) |
                                        (fs->val.pf << 13) |
                                        (fs->val.vf)) << tp->vnic_shift;
@@ -1076,6 +1073,7 @@ static int cxgb4_set_hash_filter(struct net_device *dev,
                                 struct filter_ctx *ctx)
 {
        struct adapter *adapter = netdev2adap(dev);
+       struct port_info *pi = netdev_priv(dev);
        struct tid_info *t = &adapter->tids;
        struct filter_entry *f;
        struct sk_buff *skb;
@@ -1142,13 +1140,34 @@ static int cxgb4_set_hash_filter(struct net_device *dev,
                f->fs.mask.ovlan = (fs->mask.pf << 13) | fs->mask.vf;
                f->fs.val.ovlan_vld = fs->val.pfvf_vld;
                f->fs.mask.ovlan_vld = fs->mask.pfvf_vld;
+       } else if (iconf & USE_ENC_IDX_F) {
+               if (f->fs.val.encap_vld) {
+                       struct port_info *pi = netdev_priv(f->dev);
+                       u8 match_all_mac[] = { 0, 0, 0, 0, 0, 0 };
+
+                       /* allocate MPS TCAM entry */
+                       ret = t4_alloc_encap_mac_filt(adapter, pi->viid,
+                                                     match_all_mac,
+                                                     match_all_mac,
+                                                     f->fs.val.vni,
+                                                     f->fs.mask.vni,
+                                                     0, 1, 1);
+                       if (ret < 0)
+                               goto free_atid;
+
+                       atomic_inc(&adapter->mps_encap[ret].refcnt);
+                       f->fs.val.ovlan = ret;
+                       f->fs.mask.ovlan = 0xffff;
+                       f->fs.val.ovlan_vld = 1;
+                       f->fs.mask.ovlan_vld = 1;
+               }
        }
 
        size = sizeof(struct cpl_t6_act_open_req);
        if (f->fs.type) {
                ret = cxgb4_clip_get(f->dev, (const u32 *)&f->fs.val.lip, 1);
                if (ret)
-                       goto free_atid;
+                       goto free_mps;
 
                skb = alloc_skb(size, GFP_KERNEL);
                if (!skb) {
@@ -1163,7 +1182,7 @@ static int cxgb4_set_hash_filter(struct net_device *dev,
                skb = alloc_skb(size, GFP_KERNEL);
                if (!skb) {
                        ret = -ENOMEM;
-                       goto free_atid;
+                       goto free_mps;
                }
 
                mk_act_open_req(f, skb,
@@ -1179,6 +1198,10 @@ static int cxgb4_set_hash_filter(struct net_device *dev,
 free_clip:
        cxgb4_clip_release(f->dev, (const u32 *)&f->fs.val.lip, 1);
 
+free_mps:
+       if (f->fs.val.encap_vld && f->fs.val.ovlan_vld)
+               t4_free_encap_mac_filt(adapter, pi->viid, f->fs.val.ovlan, 1);
+
 free_atid:
        cxgb4_free_atid(t, atid);
 
@@ -1360,6 +1383,27 @@ int __cxgb4_set_filter(struct net_device *dev, int filter_id,
                f->fs.mask.ovlan = (fs->mask.pf << 13) | fs->mask.vf;
                f->fs.val.ovlan_vld = fs->val.pfvf_vld;
                f->fs.mask.ovlan_vld = fs->mask.pfvf_vld;
+       } else if (iconf & USE_ENC_IDX_F) {
+               if (f->fs.val.encap_vld) {
+                       struct port_info *pi = netdev_priv(f->dev);
+                       u8 match_all_mac[] = { 0, 0, 0, 0, 0, 0 };
+
+                       /* allocate MPS TCAM entry */
+                       ret = t4_alloc_encap_mac_filt(adapter, pi->viid,
+                                                     match_all_mac,
+                                                     match_all_mac,
+                                                     f->fs.val.vni,
+                                                     f->fs.mask.vni,
+                                                     0, 1, 1);
+                       if (ret < 0)
+                               goto free_clip;
+
+                       atomic_inc(&adapter->mps_encap[ret].refcnt);
+                       f->fs.val.ovlan = ret;
+                       f->fs.mask.ovlan = 0x1ff;
+                       f->fs.val.ovlan_vld = 1;
+                       f->fs.mask.ovlan_vld = 1;
+               }
        }
 
        /* Attempt to set the filter.  If we don't succeed, we clear
@@ -1376,6 +1420,13 @@ int __cxgb4_set_filter(struct net_device *dev, int filter_id,
        }
 
        return ret;
+
+free_clip:
+       if (is_t6(adapter->params.chip) && f->fs.type)
+               cxgb4_clip_release(f->dev, (const u32 *)&f->fs.val.lip, 1);
+       cxgb4_clear_ftid(&adapter->tids, filter_id,
+                        fs->type ? PF_INET6 : PF_INET, chip_ver);
+       return ret;
 }
 
 static int cxgb4_del_hash_filter(struct net_device *dev, int filter_id,
index c54fd18..513e1d3 100644 (file)
@@ -301,14 +301,14 @@ void t4_os_link_changed(struct adapter *adapter, int port_id, int link_stat)
        }
 }
 
-void t4_os_portmod_changed(const struct adapter *adap, int port_id)
+void t4_os_portmod_changed(struct adapter *adap, int port_id)
 {
        static const char *mod_str[] = {
                NULL, "LR", "SR", "ER", "passive DA", "active DA", "LRM"
        };
 
-       const struct net_device *dev = adap->port[port_id];
-       const struct port_info *pi = netdev_priv(dev);
+       struct net_device *dev = adap->port[port_id];
+       struct port_info *pi = netdev_priv(dev);
 
        if (pi->mod_type == FW_PORT_MOD_TYPE_NONE)
                netdev_info(dev, "port module unplugged\n");
@@ -325,6 +325,11 @@ void t4_os_portmod_changed(const struct adapter *adap, int port_id)
        else
                netdev_info(dev, "%s: unknown module type %d inserted\n",
                            dev->name, pi->mod_type);
+
+       /* If the interface is running, then we'll need any "sticky" Link
+        * Parameters redone with a new Transceiver Module.
+        */
+       pi->link_cfg.redo_l1cfg = netif_running(dev);
 }
 
 int dbfifo_int_thresh = 10; /* 10 == 640 entry threshold */
@@ -5276,13 +5281,9 @@ static int cxgb4_iov_configure(struct pci_dev *pdev, int num_vfs)
        u32 pcie_fw;
 
        pcie_fw = readl(adap->regs + PCIE_FW_A);
-       /* Check if cxgb4 is the MASTER and fw is initialized */
-       if (num_vfs &&
-           (!(pcie_fw & PCIE_FW_INIT_F) ||
-           !(pcie_fw & PCIE_FW_MASTER_VLD_F) ||
-           PCIE_FW_MASTER_G(pcie_fw) != CXGB4_UNIFIED_PF)) {
-               dev_warn(&pdev->dev,
-                        "cxgb4 driver needs to be MASTER to support SRIOV\n");
+       /* Check if fw is initialized */
+       if (!(pcie_fw & PCIE_FW_INIT_F)) {
+               dev_warn(&pdev->dev, "Device not initialized\n");
                return -EOPNOTSUPP;
        }
 
@@ -5558,6 +5559,16 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (err)
                goto out_free_adapter;
 
+       if (is_kdump_kernel()) {
+               /* Collect hardware state and append to /proc/vmcore */
+               err = cxgb4_cudbg_vmcore_add_dump(adapter);
+               if (err) {
+                       dev_warn(adapter->pdev_dev,
+                                "Fail collecting vmcore device dump, err: %d. Continuing\n",
+                                err);
+                       err = 0;
+               }
+       }
 
        if (!is_t4(adapter->params.chip)) {
                s_qpp = (QUEUESPERPAGEPF0_S +
index 3656336..3ddd2c4 100644 (file)
@@ -194,6 +194,23 @@ static void cxgb4_process_flow_match(struct net_device *dev,
                fs->mask.tos = mask->tos;
        }
 
+       if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+               struct flow_dissector_key_keyid *key, *mask;
+
+               key = skb_flow_dissector_target(cls->dissector,
+                                               FLOW_DISSECTOR_KEY_ENC_KEYID,
+                                               cls->key);
+               mask = skb_flow_dissector_target(cls->dissector,
+                                                FLOW_DISSECTOR_KEY_ENC_KEYID,
+                                                cls->mask);
+               fs->val.vni = be32_to_cpu(key->keyid);
+               fs->mask.vni = be32_to_cpu(mask->keyid);
+               if (fs->mask.vni) {
+                       fs->val.encap_vld = 1;
+                       fs->mask.encap_vld = 1;
+               }
+       }
+
        if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
                struct flow_dissector_key_vlan *key, *mask;
                u16 vlan_tci, vlan_tci_mask;
@@ -247,6 +264,7 @@ static int cxgb4_validate_flow_match(struct net_device *dev,
              BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
              BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
              BIT(FLOW_DISSECTOR_KEY_PORTS) |
+             BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
              BIT(FLOW_DISSECTOR_KEY_VLAN) |
              BIT(FLOW_DISSECTOR_KEY_IP))) {
                netdev_warn(dev, "Unsupported key used: 0x%x\n",
index 1817a03..77c2c53 100644 (file)
@@ -491,7 +491,7 @@ u64 cxgb4_select_ntuple(struct net_device *dev,
        if (tp->protocol_shift >= 0)
                ntuple |= (u64)IPPROTO_TCP << tp->protocol_shift;
 
-       if (tp->vnic_shift >= 0) {
+       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);
index 0f87e97..276f223 100644 (file)
@@ -1411,8 +1411,9 @@ out_free: dev_kfree_skb_any(skb);
        end = (u64 *)wr + flits;
 
        len = immediate ? skb->len : 0;
+       len += sizeof(*cpl);
        if (ssi->gso_size) {
-               struct cpl_tx_pkt_lso *lso = (void *)wr;
+               struct cpl_tx_pkt_lso_core *lso = (void *)(wr + 1);
                bool v6 = (ssi->gso_type & SKB_GSO_TCPV6) != 0;
                int l3hdr_len = skb_network_header_len(skb);
                int eth_xtra_len = skb_network_offset(skb) - ETH_HLEN;
@@ -1442,20 +1443,19 @@ out_free:       dev_kfree_skb_any(skb);
                        if (skb->ip_summed == CHECKSUM_PARTIAL)
                                cntrl = hwcsum(adap->params.chip, skb);
                } else {
-                       lso->c.lso_ctrl = htonl(LSO_OPCODE_V(CPL_TX_PKT_LSO) |
-                                         LSO_FIRST_SLICE_F | LSO_LAST_SLICE_F |
-                                         LSO_IPV6_V(v6) |
-                                         LSO_ETHHDR_LEN_V(eth_xtra_len / 4) |
-                                         LSO_IPHDR_LEN_V(l3hdr_len / 4) |
-                                         LSO_TCPHDR_LEN_V(tcp_hdr(skb)->doff));
-                       lso->c.ipid_ofst = htons(0);
-                       lso->c.mss = htons(ssi->gso_size);
-                       lso->c.seqno_offset = htonl(0);
+                       lso->lso_ctrl = htonl(LSO_OPCODE_V(CPL_TX_PKT_LSO) |
+                                       LSO_FIRST_SLICE_F | LSO_LAST_SLICE_F |
+                                       LSO_IPV6_V(v6) |
+                                       LSO_ETHHDR_LEN_V(eth_xtra_len / 4) |
+                                       LSO_IPHDR_LEN_V(l3hdr_len / 4) |
+                                       LSO_TCPHDR_LEN_V(tcp_hdr(skb)->doff));
+                       lso->ipid_ofst = htons(0);
+                       lso->mss = htons(ssi->gso_size);
+                       lso->seqno_offset = htonl(0);
                        if (is_t4(adap->params.chip))
-                               lso->c.len = htonl(skb->len);
+                               lso->len = htonl(skb->len);
                        else
-                               lso->c.len =
-                                       htonl(LSO_T5_XFER_SIZE_V(skb->len));
+                               lso->len = htonl(LSO_T5_XFER_SIZE_V(skb->len));
                        cpl = (void *)(lso + 1);
 
                        if (CHELSIO_CHIP_VERSION(adap->params.chip)
@@ -1484,7 +1484,6 @@ out_free: dev_kfree_skb_any(skb);
                q->tso++;
                q->tx_cso += ssi->gso_segs;
        } else {
-               len += sizeof(*cpl);
                if (ptp_enabled)
                        op = FW_PTP_TX_PKT_WR;
                else
@@ -1538,7 +1537,7 @@ out_free: dev_kfree_skb_any(skb);
                if (last_desc >= q->q.size)
                        last_desc -= q->q.size;
                q->q.sdesc[last_desc].skb = skb;
-               q->q.sdesc[last_desc].sgl = (struct ulptx_sgl *)(cpl + 1);
+               q->q.sdesc[last_desc].sgl = (struct ulptx_sgl *)sgl;
        }
 
        txq_advance(&q->q, ndesc);
index 7cb3ef4..704f696 100644 (file)
@@ -3941,8 +3941,8 @@ static fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16)
        CAP16_TO_CAP32(FC_RX);
        CAP16_TO_CAP32(FC_TX);
        CAP16_TO_CAP32(ANEG);
-       CAP16_TO_CAP32(MDIX);
        CAP16_TO_CAP32(MDIAUTO);
+       CAP16_TO_CAP32(MDISTRAIGHT);
        CAP16_TO_CAP32(FEC_RS);
        CAP16_TO_CAP32(FEC_BASER_RS);
        CAP16_TO_CAP32(802_3_PAUSE);
@@ -3982,8 +3982,8 @@ static fw_port_cap16_t fwcaps32_to_caps16(fw_port_cap32_t caps32)
        CAP32_TO_CAP16(802_3_PAUSE);
        CAP32_TO_CAP16(802_3_ASM_DIR);
        CAP32_TO_CAP16(ANEG);
-       CAP32_TO_CAP16(MDIX);
        CAP32_TO_CAP16(MDIAUTO);
+       CAP32_TO_CAP16(MDISTRAIGHT);
        CAP32_TO_CAP16(FEC_RS);
        CAP32_TO_CAP16(FEC_BASER_RS);
 
@@ -4058,14 +4058,16 @@ static inline fw_port_cap32_t cc_to_fwcap_fec(enum cc_fec cc_fec)
  *     - If auto-negotiation is off set the MAC to the proper speed/duplex/FC,
  *       otherwise do it later based on the outcome of auto-negotiation.
  */
-int t4_link_l1cfg(struct adapter *adapter, unsigned int mbox,
-                 unsigned int port, struct link_config *lc)
+int t4_link_l1cfg_core(struct adapter *adapter, unsigned int mbox,
+                      unsigned int port, struct link_config *lc,
+                      bool sleep_ok, int timeout)
 {
        unsigned int fw_caps = adapter->params.fw_caps_support;
-       struct fw_port_cmd cmd;
-       unsigned int fw_mdi = FW_PORT_CAP32_MDI_V(FW_PORT_CAP32_MDI_AUTO);
        fw_port_cap32_t fw_fc, cc_fec, fw_fec, rcap;
+       struct fw_port_cmd cmd;
+       unsigned int fw_mdi;
 
+       fw_mdi = (FW_PORT_CAP32_MDI_V(FW_PORT_CAP32_MDI_AUTO) & lc->pcaps);
        /* Convert driver coding of Pause Frame Flow Control settings into the
         * Firmware's API.
         */
@@ -4087,7 +4089,7 @@ int t4_link_l1cfg(struct adapter *adapter, unsigned int mbox,
        /* Figure out what our Requested Port Capabilities are going to be.
         */
        if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) {
-               rcap = (lc->pcaps & ADVERT_MASK) | fw_fc | fw_fec;
+               rcap = lc->acaps | fw_fc | fw_fec;
                lc->fc = lc->requested_fc & ~PAUSE_AUTONEG;
                lc->fec = cc_fec;
        } else if (lc->autoneg == AUTONEG_DISABLE) {
@@ -4113,7 +4115,8 @@ int t4_link_l1cfg(struct adapter *adapter, unsigned int mbox,
                cmd.u.l1cfg.rcap = cpu_to_be32(fwcaps32_to_caps16(rcap));
        else
                cmd.u.l1cfg32.rcap32 = cpu_to_be32(rcap);
-       return t4_wr_mbox(adapter, mbox, &cmd, sizeof(cmd), NULL);
+       return t4_wr_mbox_meat_timeout(adapter, mbox, &cmd, sizeof(cmd), NULL,
+                                      sleep_ok, timeout);
 }
 
 /**
@@ -7512,6 +7515,43 @@ int t4_set_rxmode(struct adapter *adap, unsigned int mbox, unsigned int viid,
        return t4_wr_mbox_meat(adap, mbox, &c, sizeof(c), NULL, sleep_ok);
 }
 
+/**
+ *      t4_free_encap_mac_filt - frees MPS entry at given index
+ *      @adap: the adapter
+ *      @viid: the VI id
+ *      @idx: index of MPS entry to be freed
+ *      @sleep_ok: call is allowed to sleep
+ *
+ *      Frees the MPS entry at supplied index
+ *
+ *      Returns a negative error number or zero on success
+ */
+int t4_free_encap_mac_filt(struct adapter *adap, unsigned int viid,
+                          int idx, bool sleep_ok)
+{
+       struct fw_vi_mac_exact *p;
+       u8 addr[] = {0, 0, 0, 0, 0, 0};
+       struct fw_vi_mac_cmd c;
+       int ret = 0;
+       u32 exact;
+
+       memset(&c, 0, sizeof(c));
+       c.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_MAC_CMD) |
+                                  FW_CMD_REQUEST_F | FW_CMD_WRITE_F |
+                                  FW_CMD_EXEC_V(0) |
+                                  FW_VI_MAC_CMD_VIID_V(viid));
+       exact = FW_VI_MAC_CMD_ENTRY_TYPE_V(FW_VI_MAC_TYPE_EXACTMAC);
+       c.freemacs_to_len16 = cpu_to_be32(FW_VI_MAC_CMD_FREEMACS_V(0) |
+                                         exact |
+                                         FW_CMD_LEN16_V(1));
+       p = c.u.exact;
+       p->valid_to_idx = cpu_to_be16(FW_VI_MAC_CMD_VALID_F |
+                                     FW_VI_MAC_CMD_IDX_V(idx));
+       memcpy(p->macaddr, addr, sizeof(p->macaddr));
+       ret = t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, sleep_ok);
+       return ret;
+}
+
 /**
  *     t4_free_raw_mac_filt - Frees a raw mac entry in mps tcam
  *     @adap: the adapter
@@ -7562,6 +7602,55 @@ int t4_free_raw_mac_filt(struct adapter *adap, unsigned int viid,
        return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, sleep_ok);
 }
 
+/**
+ *      t4_alloc_encap_mac_filt - Adds a mac entry in mps tcam with VNI support
+ *      @adap: the adapter
+ *      @viid: the VI id
+ *      @mac: the MAC address
+ *      @mask: the mask
+ *      @vni: the VNI id for the tunnel protocol
+ *      @vni_mask: mask for the VNI id
+ *      @dip_hit: to enable DIP match for the MPS entry
+ *      @lookup_type: MAC address for inner (1) or outer (0) header
+ *      @sleep_ok: call is allowed to sleep
+ *
+ *      Allocates an MPS entry with specified MAC address and VNI value.
+ *
+ *      Returns a negative error number or the allocated index for this mac.
+ */
+int t4_alloc_encap_mac_filt(struct adapter *adap, unsigned int viid,
+                           const u8 *addr, const u8 *mask, unsigned int vni,
+                           unsigned int vni_mask, u8 dip_hit, u8 lookup_type,
+                           bool sleep_ok)
+{
+       struct fw_vi_mac_cmd c;
+       struct fw_vi_mac_vni *p = c.u.exact_vni;
+       int ret = 0;
+       u32 val;
+
+       memset(&c, 0, sizeof(c));
+       c.op_to_viid = cpu_to_be32(FW_CMD_OP_V(FW_VI_MAC_CMD) |
+                                  FW_CMD_REQUEST_F | FW_CMD_WRITE_F |
+                                  FW_VI_MAC_CMD_VIID_V(viid));
+       val = FW_CMD_LEN16_V(1) |
+             FW_VI_MAC_CMD_ENTRY_TYPE_V(FW_VI_MAC_TYPE_EXACTMAC_VNI);
+       c.freemacs_to_len16 = cpu_to_be32(val);
+       p->valid_to_idx = cpu_to_be16(FW_VI_MAC_CMD_VALID_F |
+                                     FW_VI_MAC_CMD_IDX_V(FW_VI_MAC_ADD_MAC));
+       memcpy(p->macaddr, addr, sizeof(p->macaddr));
+       memcpy(p->macaddr_mask, mask, sizeof(p->macaddr_mask));
+
+       p->lookup_type_to_vni =
+               cpu_to_be32(FW_VI_MAC_CMD_VNI_V(vni) |
+                           FW_VI_MAC_CMD_DIP_HIT_V(dip_hit) |
+                           FW_VI_MAC_CMD_LOOKUP_TYPE_V(lookup_type));
+       p->vni_mask_pkd = cpu_to_be32(FW_VI_MAC_CMD_VNI_MASK_V(vni_mask));
+       ret = t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, sleep_ok);
+       if (ret == 0)
+               ret = FW_VI_MAC_CMD_IDX_G(be16_to_cpu(p->valid_to_idx));
+       return ret;
+}
+
 /**
  *     t4_alloc_raw_mac_filt - Adds a mac entry in mps tcam
  *     @adap: the adapter
@@ -8249,6 +8338,9 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl)
        fc = fwcap_to_cc_pause(linkattr);
        speed = fwcap_to_speed(linkattr);
 
+       lc->new_module = false;
+       lc->redo_l1cfg = false;
+
        if (mod_type != pi->mod_type) {
                /* With the newer SFP28 and QSFP28 Transceiver Module Types,
                 * various fundamental Port Capabilities which used to be
@@ -8283,6 +8375,8 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl)
                pi->port_type = port_type;
 
                pi->mod_type = mod_type;
+
+               lc->new_module = t4_is_inserted_mod_type(mod_type);
                t4_os_portmod_changed(adapter, pi->port_id);
        }
 
@@ -8315,6 +8409,26 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl)
 
                t4_os_link_changed(adapter, pi->port_id, link_ok);
        }
+
+       if (lc->new_module && lc->redo_l1cfg) {
+               struct link_config old_lc;
+               int ret;
+
+               /* Save the current L1 Configuration and restore it if an
+                * error occurs.  We probably should fix the l1_cfg*()
+                * routines not to change the link_config when an error
+                * occurs ...
+                */
+               old_lc = *lc;
+               ret = t4_link_l1cfg_ns(adapter, adapter->mbox, pi->lport, lc);
+               if (ret) {
+                       *lc = old_lc;
+                       dev_warn(adapter->pdev_dev,
+                                "Attempt to update new Transceiver Module settings failed\n");
+               }
+       }
+       lc->new_module = false;
+       lc->redo_l1cfg = false;
 }
 
 /**
index adacc63..c7f8d04 100644 (file)
@@ -212,6 +212,8 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
        CH_PCI_ID_TABLE_FENTRY(0x6085), /* Custom T6240-SO */
        CH_PCI_ID_TABLE_FENTRY(0x6086), /* Custom T6225-SO-CR */
        CH_PCI_ID_TABLE_FENTRY(0x6087), /* Custom T6225-CR */
+       CH_PCI_ID_TABLE_FENTRY(0x6088), /* Custom T62100-CR */
+       CH_PCI_ID_TABLE_FENTRY(0x6089), /* Custom T62100-KR */
 CH_PCI_DEVICE_ID_TABLE_DEFINE_END;
 
 #endif /* __T4_PCI_ID_TBL_H__ */
index 276fdf2..6b55aa2 100644 (file)
 #define VNIC_V(x) ((x) << VNIC_S)
 #define VNIC_F    VNIC_V(1U)
 
+#define USE_ENC_IDX_S          13
+#define USE_ENC_IDX_V(x)       ((x) << USE_ENC_IDX_S)
+#define USE_ENC_IDX_F          USE_ENC_IDX_V(1U)
+
 #define CSUM_HAS_PSEUDO_HDR_S    10
 #define CSUM_HAS_PSEUDO_HDR_V(x) ((x) << CSUM_HAS_PSEUDO_HDR_S)
 #define CSUM_HAS_PSEUDO_HDR_F    CSUM_HAS_PSEUDO_HDR_V(1U)
 #define LE_DB_HASH_TID_BASE_A 0x19c30
 #define LE_DB_HASH_TBL_BASE_ADDR_A 0x19c30
 #define LE_DB_INT_CAUSE_A 0x19c3c
+#define LE_DB_CLCAM_TID_BASE_A 0x19df4
 #define LE_DB_TID_HASHBASE_A 0x19df8
 #define T6_LE_DB_HASH_TID_BASE_A 0x19df8
 
index 0e007ee..2d91480 100644 (file)
@@ -2158,6 +2158,14 @@ struct fw_vi_mac_cmd {
                        __be64 data0m_pkd;
                        __be32 data1m[2];
                } raw;
+               struct fw_vi_mac_vni {
+                       __be16 valid_to_idx;
+                       __u8 macaddr[6];
+                       __be16 r7;
+                       __u8 macaddr_mask[6];
+                       __be32 lookup_type_to_vni;
+                       __be32 vni_mask_pkd;
+               } exact_vni[2];
        } u;
 };
 
@@ -2205,6 +2213,32 @@ struct fw_vi_mac_cmd {
 #define FW_VI_MAC_CMD_RAW_IDX_G(x)      \
        (((x) >> FW_VI_MAC_CMD_RAW_IDX_S) & FW_VI_MAC_CMD_RAW_IDX_M)
 
+#define FW_VI_MAC_CMD_LOOKUP_TYPE_S    31
+#define FW_VI_MAC_CMD_LOOKUP_TYPE_M    0x1
+#define FW_VI_MAC_CMD_LOOKUP_TYPE_V(x) ((x) << FW_VI_MAC_CMD_LOOKUP_TYPE_S)
+#define FW_VI_MAC_CMD_LOOKUP_TYPE_G(x) \
+       (((x) >> FW_VI_MAC_CMD_LOOKUP_TYPE_S) & FW_VI_MAC_CMD_LOOKUP_TYPE_M)
+#define FW_VI_MAC_CMD_LOOKUP_TYPE_F    FW_VI_MAC_CMD_LOOKUP_TYPE_V(1U)
+
+#define FW_VI_MAC_CMD_DIP_HIT_S                30
+#define FW_VI_MAC_CMD_DIP_HIT_M                0x1
+#define FW_VI_MAC_CMD_DIP_HIT_V(x)     ((x) << FW_VI_MAC_CMD_DIP_HIT_S)
+#define FW_VI_MAC_CMD_DIP_HIT_G(x)     \
+       (((x) >> FW_VI_MAC_CMD_DIP_HIT_S) & FW_VI_MAC_CMD_DIP_HIT_M)
+#define FW_VI_MAC_CMD_DIP_HIT_F                FW_VI_MAC_CMD_DIP_HIT_V(1U)
+
+#define FW_VI_MAC_CMD_VNI_S            0
+#define FW_VI_MAC_CMD_VNI_M            0xffffff
+#define FW_VI_MAC_CMD_VNI_V(x)         ((x) << FW_VI_MAC_CMD_VNI_S)
+#define FW_VI_MAC_CMD_VNI_G(x)         \
+       (((x) >> FW_VI_MAC_CMD_VNI_S) & FW_VI_MAC_CMD_VNI_M)
+
+#define FW_VI_MAC_CMD_VNI_MASK_S       0
+#define FW_VI_MAC_CMD_VNI_MASK_M       0xffffff
+#define FW_VI_MAC_CMD_VNI_MASK_V(x)    ((x) << FW_VI_MAC_CMD_VNI_MASK_S)
+#define FW_VI_MAC_CMD_VNI_MASK_G(x)    \
+       (((x) >> FW_VI_MAC_CMD_VNI_MASK_S) & FW_VI_MAC_CMD_VNI_MASK_M)
+
 #define FW_RXMODE_MTU_NO_CHG   65535
 
 struct fw_vi_rxmode_cmd {
@@ -2437,8 +2471,8 @@ enum fw_port_cap {
        FW_PORT_CAP_FC_RX               = 0x0040,
        FW_PORT_CAP_FC_TX               = 0x0080,
        FW_PORT_CAP_ANEG                = 0x0100,
-       FW_PORT_CAP_MDIX                = 0x0200,
-       FW_PORT_CAP_MDIAUTO             = 0x0400,
+       FW_PORT_CAP_MDIAUTO             = 0x0200,
+       FW_PORT_CAP_MDISTRAIGHT         = 0x0400,
        FW_PORT_CAP_FEC_RS              = 0x0800,
        FW_PORT_CAP_FEC_BASER_RS        = 0x1000,
        FW_PORT_CAP_FEC_RESERVED        = 0x2000,
@@ -2481,8 +2515,8 @@ enum fw_port_mdi {
 #define        FW_PORT_CAP32_802_3_PAUSE       0x00040000UL
 #define        FW_PORT_CAP32_802_3_ASM_DIR     0x00080000UL
 #define        FW_PORT_CAP32_ANEG              0x00100000UL
-#define        FW_PORT_CAP32_MDIX              0x00200000UL
-#define        FW_PORT_CAP32_MDIAUTO           0x00400000UL
+#define        FW_PORT_CAP32_MDIAUTO           0x00200000UL
+#define        FW_PORT_CAP32_MDISTRAIGHT       0x00400000UL
 #define        FW_PORT_CAP32_FEC_RS            0x00800000UL
 #define        FW_PORT_CAP32_FEC_BASER_RS      0x01000000UL
 #define        FW_PORT_CAP32_FEC_RESERVED1     0x02000000UL
index 798695b..3017f78 100644 (file)
@@ -341,8 +341,8 @@ static fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16)
        CAP16_TO_CAP32(FC_RX);
        CAP16_TO_CAP32(FC_TX);
        CAP16_TO_CAP32(ANEG);
-       CAP16_TO_CAP32(MDIX);
        CAP16_TO_CAP32(MDIAUTO);
+       CAP16_TO_CAP32(MDISTRAIGHT);
        CAP16_TO_CAP32(FEC_RS);
        CAP16_TO_CAP32(FEC_BASER_RS);
        CAP16_TO_CAP32(802_3_PAUSE);
index 8bb0db9..00a5727 100644 (file)
@@ -1246,8 +1246,7 @@ error:
        mdiobus_unregister(priv->mdio);
        mdiobus_free(priv->mdio);
 free2:
-       if (priv->clk)
-               clk_disable_unprepare(priv->clk);
+       clk_disable_unprepare(priv->clk);
 free:
        free_netdev(netdev);
 out:
@@ -1271,8 +1270,7 @@ static int ethoc_remove(struct platform_device *pdev)
                        mdiobus_unregister(priv->mdio);
                        mdiobus_free(priv->mdio);
                }
-               if (priv->clk)
-                       clk_disable_unprepare(priv->clk);
+               clk_disable_unprepare(priv->clk);
                unregister_netdev(netdev);
                free_netdev(netdev);
        }
index 6e490fd..a580a3d 100644 (file)
@@ -22,7 +22,7 @@ if NET_VENDOR_FREESCALE
 config FEC
        tristate "FEC ethernet controller (of ColdFire and some i.MX CPUs)"
        depends on (M523x || M527x || M5272 || M528x || M520x || M532x || \
-                  ARCH_MXC || SOC_IMX28)
+                  ARCH_MXC || SOC_IMX28 || COMPILE_TEST)
        default ARCH_MXC || SOC_IMX28 if ARM
        select PHYLIB
        imply PTP_1588_CLOCK
index e7381f8..4778b66 100644 (file)
@@ -21,7 +21,7 @@
 
 #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
     defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
-    defined(CONFIG_ARM64)
+    defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
 /*
  *     Just figures, Motorola would have to change the offsets for
  *     registers in the same peripheral device on different models
index d4604bc..4358f58 100644 (file)
@@ -2052,13 +2052,9 @@ static int fec_enet_mii_init(struct platform_device *pdev)
        fep->mii_bus->parent = &pdev->dev;
 
        node = of_get_child_by_name(pdev->dev.of_node, "mdio");
-       if (node) {
-               err = of_mdiobus_register(fep->mii_bus, node);
+       err = of_mdiobus_register(fep->mii_bus, node);
+       if (node)
                of_node_put(node);
-       } else {
-               err = mdiobus_register(fep->mii_bus);
-       }
-
        if (err)
                goto err_out_free_mdiobus;
 
@@ -2111,7 +2107,7 @@ static int fec_enet_get_regs_len(struct net_device *ndev)
 /* List of registers that can be safety be read to dump them with ethtool */
 #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
        defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
-       defined(CONFIG_ARM64)
+       defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
 static u32 fec_enet_register_offset[] = {
        FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0,
        FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL,
index 02145f2..63d7dbf 100644 (file)
@@ -50,13 +50,22 @@ static int hnae3_match_n_instantiate(struct hnae3_client *client,
        /* now, (un-)instantiate client by calling lower layer */
        if (is_reg) {
                ret = ae_dev->ops->init_client_instance(client, ae_dev);
-               if (ret)
+               if (ret) {
                        dev_err(&ae_dev->pdev->dev,
                                "fail to instantiate client\n");
-               return ret;
+                       return ret;
+               }
+
+               hnae_set_bit(ae_dev->flag, HNAE3_CLIENT_INITED_B, 1);
+               return 0;
+       }
+
+       if (hnae_get_bit(ae_dev->flag, HNAE3_CLIENT_INITED_B)) {
+               ae_dev->ops->uninit_client_instance(client, ae_dev);
+
+               hnae_set_bit(ae_dev->flag, HNAE3_CLIENT_INITED_B, 0);
        }
 
-       ae_dev->ops->uninit_client_instance(client, ae_dev);
        return 0;
 }
 
@@ -89,7 +98,7 @@ int hnae3_register_client(struct hnae3_client *client)
 exit:
        mutex_unlock(&hnae3_common_lock);
 
-       return ret;
+       return 0;
 }
 EXPORT_SYMBOL(hnae3_register_client);
 
@@ -112,7 +121,7 @@ EXPORT_SYMBOL(hnae3_unregister_client);
  * @ae_algo: AE algorithm
  * NOTE: the duplicated name will not be checked
  */
-int hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo)
+void hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo)
 {
        const struct pci_device_id *id;
        struct hnae3_ae_dev *ae_dev;
@@ -151,8 +160,6 @@ int hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo)
        }
 
        mutex_unlock(&hnae3_common_lock);
-
-       return ret;
 }
 EXPORT_SYMBOL(hnae3_register_ae_algo);
 
@@ -168,6 +175,9 @@ void hnae3_unregister_ae_algo(struct hnae3_ae_algo *ae_algo)
        mutex_lock(&hnae3_common_lock);
        /* Check if there are matched ae_dev */
        list_for_each_entry(ae_dev, &hnae3_ae_dev_list, node) {
+               if (!hnae_get_bit(ae_dev->flag, HNAE3_DEV_INITED_B))
+                       continue;
+
                id = pci_match_id(ae_algo->pdev_id_table, ae_dev->pdev);
                if (!id)
                        continue;
@@ -191,22 +201,14 @@ EXPORT_SYMBOL(hnae3_unregister_ae_algo);
  * @ae_dev: the AE device
  * NOTE: the duplicated name will not be checked
  */
-int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
+void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
 {
        const struct pci_device_id *id;
        struct hnae3_ae_algo *ae_algo;
        struct hnae3_client *client;
-       int ret = 0, lock_acquired;
+       int ret = 0;
 
-       /* we can get deadlocked if SRIOV is being enabled in context to probe
-        * and probe gets called again in same context. This can happen when
-        * pci_enable_sriov() is called to create VFs from PF probes context.
-        * Therefore, for simplicity uniformly defering further probing in all
-        * cases where we detect contention.
-        */
-       lock_acquired = mutex_trylock(&hnae3_common_lock);
-       if (!lock_acquired)
-               return -EPROBE_DEFER;
+       mutex_lock(&hnae3_common_lock);
 
        list_add_tail(&ae_dev->node, &hnae3_ae_dev_list);
 
@@ -220,7 +222,6 @@ int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
 
                if (!ae_dev->ops) {
                        dev_err(&ae_dev->pdev->dev, "ae_dev ops are null\n");
-                       ret = -EOPNOTSUPP;
                        goto out_err;
                }
 
@@ -247,8 +248,6 @@ int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev)
 
 out_err:
        mutex_unlock(&hnae3_common_lock);
-
-       return ret;
 }
 EXPORT_SYMBOL(hnae3_register_ae_dev);
 
@@ -264,6 +263,9 @@ void hnae3_unregister_ae_dev(struct hnae3_ae_dev *ae_dev)
        mutex_lock(&hnae3_common_lock);
        /* Check if there are matched ae_algo */
        list_for_each_entry(ae_algo, &hnae3_ae_algo_list, node) {
+               if (!hnae_get_bit(ae_dev->flag, HNAE3_DEV_INITED_B))
+                       continue;
+
                id = pci_match_id(ae_algo->pdev_id_table, ae_dev->pdev);
                if (!id)
                        continue;
@@ -283,3 +285,4 @@ EXPORT_SYMBOL(hnae3_unregister_ae_dev);
 MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("HNAE3(Hisilicon Network Acceleration Engine) Framework");
+MODULE_VERSION(HNAE3_MOD_VERSION);
index 804ea83..45c571e 100644 (file)
@@ -36,6 +36,8 @@
 #include <linux/pci.h>
 #include <linux/types.h>
 
+#define HNAE3_MOD_VERSION "1.0"
+
 /* Device IDs */
 #define HNAE3_DEV_ID_GE                                0xA220
 #define HNAE3_DEV_ID_25GE                      0xA221
@@ -52,6 +54,7 @@
 #define HNAE3_DEV_INITED_B                     0x0
 #define HNAE3_DEV_SUPPORT_ROCE_B               0x1
 #define HNAE3_DEV_SUPPORT_DCB_B                        0x2
+#define HNAE3_CLIENT_INITED_B                  0x3
 
 #define HNAE3_DEV_SUPPORT_ROCE_DCB_BITS (BIT(HNAE3_DEV_SUPPORT_DCB_B) |\
                BIT(HNAE3_DEV_SUPPORT_ROCE_B))
@@ -514,11 +517,11 @@ struct hnae3_handle {
 #define hnae_get_bit(origin, shift) \
        hnae_get_field((origin), (0x1 << (shift)), (shift))
 
-int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev);
+void hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev);
 void hnae3_unregister_ae_dev(struct hnae3_ae_dev *ae_dev);
 
 void hnae3_unregister_ae_algo(struct hnae3_ae_algo *ae_algo);
-int hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo);
+void hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo);
 
 void hnae3_unregister_client(struct hnae3_client *client);
 int hnae3_register_client(struct hnae3_client *client);
index 4031174..cac5195 100644 (file)
@@ -1487,6 +1487,45 @@ static const struct net_device_ops hns3_nic_netdev_ops = {
        .ndo_set_vf_vlan        = hns3_ndo_set_vf_vlan,
 };
 
+static bool hns3_is_phys_func(struct pci_dev *pdev)
+{
+       u32 dev_id = pdev->device;
+
+       switch (dev_id) {
+       case HNAE3_DEV_ID_GE:
+       case HNAE3_DEV_ID_25GE:
+       case HNAE3_DEV_ID_25GE_RDMA:
+       case HNAE3_DEV_ID_25GE_RDMA_MACSEC:
+       case HNAE3_DEV_ID_50GE_RDMA:
+       case HNAE3_DEV_ID_50GE_RDMA_MACSEC:
+       case HNAE3_DEV_ID_100G_RDMA_MACSEC:
+               return true;
+       case HNAE3_DEV_ID_100G_VF:
+       case HNAE3_DEV_ID_100G_RDMA_DCB_PFC_VF:
+               return false;
+       default:
+               dev_warn(&pdev->dev, "un-recognized pci device-id %d",
+                        dev_id);
+       }
+
+       return false;
+}
+
+static void hns3_disable_sriov(struct pci_dev *pdev)
+{
+       /* If our VFs are assigned we cannot shut down SR-IOV
+        * without causing issues, so just leave the hardware
+        * available but disabled
+        */
+       if (pci_vfs_assigned(pdev)) {
+               dev_warn(&pdev->dev,
+                        "disabling driver while VFs are assigned\n");
+               return;
+       }
+
+       pci_disable_sriov(pdev);
+}
+
 /* hns3_probe - Device initialization routine
  * @pdev: PCI device information struct
  * @ent: entry in hns3_pci_tbl
@@ -1514,7 +1553,9 @@ static int hns3_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        ae_dev->dev_type = HNAE3_DEV_KNIC;
        pci_set_drvdata(pdev, ae_dev);
 
-       return hnae3_register_ae_dev(ae_dev);
+       hnae3_register_ae_dev(ae_dev);
+
+       return 0;
 }
 
 /* hns3_remove - Device removal routine
@@ -1524,14 +1565,51 @@ static void hns3_remove(struct pci_dev *pdev)
 {
        struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev);
 
+       if (hns3_is_phys_func(pdev) && IS_ENABLED(CONFIG_PCI_IOV))
+               hns3_disable_sriov(pdev);
+
        hnae3_unregister_ae_dev(ae_dev);
 }
 
+/**
+ * hns3_pci_sriov_configure
+ * @pdev: pointer to a pci_dev structure
+ * @num_vfs: number of VFs to allocate
+ *
+ * Enable or change the number of VFs. Called when the user updates the number
+ * of VFs in sysfs.
+ **/
+static int hns3_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+       int ret;
+
+       if (!(hns3_is_phys_func(pdev) && IS_ENABLED(CONFIG_PCI_IOV))) {
+               dev_warn(&pdev->dev, "Can not config SRIOV\n");
+               return -EINVAL;
+       }
+
+       if (num_vfs) {
+               ret = pci_enable_sriov(pdev, num_vfs);
+               if (ret)
+                       dev_err(&pdev->dev, "SRIOV enable failed %d\n", ret);
+               else
+                       return num_vfs;
+       } else if (!pci_vfs_assigned(pdev)) {
+               pci_disable_sriov(pdev);
+       } else {
+               dev_warn(&pdev->dev,
+                        "Unable to free VFs because some are assigned to VMs.\n");
+       }
+
+       return 0;
+}
+
 static struct pci_driver hns3_driver = {
        .name     = hns3_driver_name,
        .id_table = hns3_pci_tbl,
        .probe    = hns3_probe,
        .remove   = hns3_remove,
+       .sriov_configure = hns3_pci_sriov_configure,
 };
 
 /* set default feature to hns3 */
@@ -1876,106 +1954,6 @@ hns3_nic_alloc_rx_buffers(struct hns3_enet_ring *ring, int cleand_count)
        writel_relaxed(i, ring->tqp->io_base + HNS3_RING_RX_RING_HEAD_REG);
 }
 
-/* hns3_nic_get_headlen - determine size of header for LRO/GRO
- * @data: pointer to the start of the headers
- * @max: total length of section to find headers in
- *
- * This function is meant to determine the length of headers that will
- * be recognized by hardware for LRO, GRO, and RSC offloads.  The main
- * motivation of doing this is to only perform one pull for IPv4 TCP
- * packets so that we can do basic things like calculating the gso_size
- * based on the average data per packet.
- */
-static unsigned int hns3_nic_get_headlen(unsigned char *data, u32 flag,
-                                        unsigned int max_size)
-{
-       unsigned char *network;
-       u8 hlen;
-
-       /* This should never happen, but better safe than sorry */
-       if (max_size < ETH_HLEN)
-               return max_size;
-
-       /* Initialize network frame pointer */
-       network = data;
-
-       /* Set first protocol and move network header forward */
-       network += ETH_HLEN;
-
-       /* Handle any vlan tag if present */
-       if (hnae_get_field(flag, HNS3_RXD_VLAN_M, HNS3_RXD_VLAN_S)
-               == HNS3_RX_FLAG_VLAN_PRESENT) {
-               if ((typeof(max_size))(network - data) > (max_size - VLAN_HLEN))
-                       return max_size;
-
-               network += VLAN_HLEN;
-       }
-
-       /* Handle L3 protocols */
-       if (hnae_get_field(flag, HNS3_RXD_L3ID_M, HNS3_RXD_L3ID_S)
-               == HNS3_RX_FLAG_L3ID_IPV4) {
-               if ((typeof(max_size))(network - data) >
-                   (max_size - sizeof(struct iphdr)))
-                       return max_size;
-
-               /* Access ihl as a u8 to avoid unaligned access on ia64 */
-               hlen = (network[0] & 0x0F) << 2;
-
-               /* Verify hlen meets minimum size requirements */
-               if (hlen < sizeof(struct iphdr))
-                       return network - data;
-
-               /* Record next protocol if header is present */
-       } else if (hnae_get_field(flag, HNS3_RXD_L3ID_M, HNS3_RXD_L3ID_S)
-               == HNS3_RX_FLAG_L3ID_IPV6) {
-               if ((typeof(max_size))(network - data) >
-                   (max_size - sizeof(struct ipv6hdr)))
-                       return max_size;
-
-               /* Record next protocol */
-               hlen = sizeof(struct ipv6hdr);
-       } else {
-               return network - data;
-       }
-
-       /* Relocate pointer to start of L4 header */
-       network += hlen;
-
-       /* Finally sort out TCP/UDP */
-       if (hnae_get_field(flag, HNS3_RXD_L4ID_M, HNS3_RXD_L4ID_S)
-               == HNS3_RX_FLAG_L4ID_TCP) {
-               if ((typeof(max_size))(network - data) >
-                   (max_size - sizeof(struct tcphdr)))
-                       return max_size;
-
-               /* Access doff as a u8 to avoid unaligned access on ia64 */
-               hlen = (network[12] & 0xF0) >> 2;
-
-               /* Verify hlen meets minimum size requirements */
-               if (hlen < sizeof(struct tcphdr))
-                       return network - data;
-
-               network += hlen;
-       } else if (hnae_get_field(flag, HNS3_RXD_L4ID_M, HNS3_RXD_L4ID_S)
-               == HNS3_RX_FLAG_L4ID_UDP) {
-               if ((typeof(max_size))(network - data) >
-                   (max_size - sizeof(struct udphdr)))
-                       return max_size;
-
-               network += sizeof(struct udphdr);
-       }
-
-       /* If everything has gone correctly network should be the
-        * data section of the packet and will be the end of the header.
-        * If not then it probably represents the end of the last recognized
-        * header.
-        */
-       if ((typeof(max_size))(network - data) < max_size)
-               return network - data;
-       else
-               return max_size;
-}
-
 static void hns3_nic_reuse_page(struct sk_buff *skb, int i,
                                struct hns3_enet_ring *ring, int pull_len,
                                struct hns3_desc_cb *desc_cb)
@@ -2175,8 +2153,8 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
                ring->stats.seg_pkt_cnt++;
                u64_stats_update_end(&ring->syncp);
 
-               pull_len = hns3_nic_get_headlen(va, l234info,
-                                               HNS3_RX_HEAD_SIZE);
+               pull_len = eth_get_headlen(va, HNS3_RX_HEAD_SIZE);
+
                memcpy(__skb_put(skb, pull_len), va,
                       ALIGN(pull_len, sizeof(long)));
 
@@ -3531,6 +3509,8 @@ static int __init hns3_init_module(void)
 
        client.ops = &client_ops;
 
+       INIT_LIST_HEAD(&client.node);
+
        ret = hnae3_register_client(&client);
        if (ret)
                return ret;
@@ -3558,3 +3538,4 @@ MODULE_DESCRIPTION("HNS3: Hisilicon Ethernet Driver");
 MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("pci:hns-nic");
+MODULE_VERSION(HNS3_MOD_VERSION);
index 98cdbd3..5b40f5a 100644 (file)
@@ -14,6 +14,8 @@
 
 #include "hnae3.h"
 
+#define HNS3_MOD_VERSION "1.0"
+
 extern const char hns3_driver_version[];
 
 enum hns3_nic_state {
index fab7068..c36d647 100644 (file)
@@ -190,7 +190,11 @@ static int hclge_cmd_csq_done(struct hclge_hw *hw)
 
 static bool hclge_is_special_opcode(u16 opcode)
 {
-       u16 spec_opcode[3] = {0x0030, 0x0031, 0x0032};
+       /* these commands have several descriptors,
+        * and use the first one to save opcode and return value
+        */
+       u16 spec_opcode[3] = {HCLGE_OPC_STATS_64_BIT,
+               HCLGE_OPC_STATS_32_BIT, HCLGE_OPC_STATS_MAC};
        int i;
 
        for (i = 0; i < ARRAY_SIZE(spec_opcode); i++) {
@@ -381,9 +385,9 @@ int hclge_cmd_init(struct hclge_dev *hdev)
 
 static void hclge_destroy_queue(struct hclge_cmq_ring *ring)
 {
-       spin_lock_bh(&ring->lock);
+       spin_lock(&ring->lock);
        hclge_free_cmd_desc(ring);
-       spin_unlock_bh(&ring->lock);
+       spin_unlock(&ring->lock);
 }
 
 void hclge_destroy_cmd_queue(struct hclge_hw *hw)
index 316ec84..2f0bbb6 100644 (file)
@@ -1473,21 +1473,8 @@ static int hclge_alloc_vport(struct hclge_dev *hdev)
        hdev->vport = vport;
        hdev->num_alloc_vport = num_vport;
 
-#ifdef CONFIG_PCI_IOV
-       /* Enable SRIOV */
-       if (hdev->num_req_vfs) {
-               dev_info(&pdev->dev, "active VFs(%d) found, enabling SRIOV\n",
-                        hdev->num_req_vfs);
-               ret = pci_enable_sriov(hdev->pdev, hdev->num_req_vfs);
-               if (ret) {
-                       hdev->num_alloc_vfs = 0;
-                       dev_err(&pdev->dev, "SRIOV enable failed %d\n",
-                               ret);
-                       return ret;
-               }
-       }
-       hdev->num_alloc_vfs = hdev->num_req_vfs;
-#endif
+       if (IS_ENABLED(CONFIG_PCI_IOV))
+               hdev->num_alloc_vfs = hdev->num_req_vfs;
 
        for (i = 0; i < num_vport; i++) {
                vport->back = hdev;
@@ -2946,21 +2933,6 @@ static void hclge_service_task(struct work_struct *work)
        hclge_service_complete(hdev);
 }
 
-static void hclge_disable_sriov(struct hclge_dev *hdev)
-{
-       /* If our VFs are assigned we cannot shut down SR-IOV
-        * without causing issues, so just leave the hardware
-        * available but disabled
-        */
-       if (pci_vfs_assigned(hdev->pdev)) {
-               dev_warn(&hdev->pdev->dev,
-                        "disabling driver while VFs are assigned\n");
-               return;
-       }
-
-       pci_disable_sriov(hdev->pdev);
-}
-
 struct hclge_vport *hclge_get_vport(struct hnae3_handle *handle)
 {
        /* VF handle has no client */
@@ -3784,6 +3756,7 @@ static int hclge_ae_start(struct hnae3_handle *handle)
        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 */
        hclge_reset_tqp_stats(handle);
@@ -3820,6 +3793,8 @@ 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);
 }
 
@@ -5325,7 +5300,7 @@ static int hclge_init_client_instance(struct hnae3_client *client,
                        vport->nic.client = client;
                        ret = client->ops->init_instance(&vport->nic);
                        if (ret)
-                               goto err;
+                               return ret;
 
                        if (hdev->roce_client &&
                            hnae3_dev_roce_supported(hdev)) {
@@ -5333,11 +5308,11 @@ static int hclge_init_client_instance(struct hnae3_client *client,
 
                                ret = hclge_init_roce_base_info(vport);
                                if (ret)
-                                       goto err;
+                                       return ret;
 
                                ret = rc->ops->init_instance(&vport->roce);
                                if (ret)
-                                       goto err;
+                                       return ret;
                        }
 
                        break;
@@ -5347,7 +5322,7 @@ static int hclge_init_client_instance(struct hnae3_client *client,
 
                        ret = client->ops->init_instance(&vport->nic);
                        if (ret)
-                               goto err;
+                               return ret;
 
                        break;
                case HNAE3_CLIENT_ROCE:
@@ -5359,18 +5334,16 @@ static int hclge_init_client_instance(struct hnae3_client *client,
                        if (hdev->roce_client && hdev->nic_client) {
                                ret = hclge_init_roce_base_info(vport);
                                if (ret)
-                                       goto err;
+                                       return ret;
 
                                ret = client->ops->init_instance(&vport->roce);
                                if (ret)
-                                       goto err;
+                                       return ret;
                        }
                }
        }
 
        return 0;
-err:
-       return ret;
 }
 
 static void hclge_uninit_client_instance(struct hnae3_client *client,
@@ -5407,7 +5380,7 @@ static int hclge_pci_init(struct hclge_dev *hdev)
        ret = pci_enable_device(pdev);
        if (ret) {
                dev_err(&pdev->dev, "failed to enable PCI device\n");
-               goto err_no_drvdata;
+               return ret;
        }
 
        ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
@@ -5445,8 +5418,6 @@ err_clr_master:
        pci_release_regions(pdev);
 err_disable_device:
        pci_disable_device(pdev);
-err_no_drvdata:
-       pci_set_drvdata(pdev, NULL);
 
        return ret;
 }
@@ -5455,6 +5426,7 @@ static void hclge_pci_uninit(struct hclge_dev *hdev)
 {
        struct pci_dev *pdev = hdev->pdev;
 
+       pcim_iounmap(pdev, hdev->hw.io_base);
        pci_free_irq_vectors(pdev);
        pci_clear_master(pdev);
        pci_release_mem_regions(pdev);
@@ -5540,7 +5512,7 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
        ret = hclge_map_tqp(hdev);
        if (ret) {
                dev_err(&pdev->dev, "Map tqp error, ret = %d.\n", ret);
-               goto err_sriov_disable;
+               goto err_msi_irq_uninit;
        }
 
        if (hdev->hw.mac.media_type == HNAE3_MEDIA_TYPE_COPPER) {
@@ -5548,7 +5520,7 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
                if (ret) {
                        dev_err(&hdev->pdev->dev,
                                "mdio config fail ret=%d\n", ret);
-                       goto err_sriov_disable;
+                       goto err_msi_irq_uninit;
                }
        }
 
@@ -5612,9 +5584,6 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
 err_mdiobus_unreg:
        if (hdev->hw.mac.phydev)
                mdiobus_unregister(hdev->hw.mac.mdio_bus);
-err_sriov_disable:
-       if (IS_ENABLED(CONFIG_PCI_IOV))
-               hclge_disable_sriov(hdev);
 err_msi_irq_uninit:
        hclge_misc_irq_uninit(hdev);
 err_msi_uninit:
@@ -5622,10 +5591,10 @@ err_msi_uninit:
 err_cmd_uninit:
        hclge_destroy_cmd_queue(&hdev->hw);
 err_pci_uninit:
+       pcim_iounmap(pdev, hdev->hw.io_base);
        pci_clear_master(pdev);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 out:
        return ret;
 }
@@ -5717,9 +5686,6 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
 
        set_bit(HCLGE_STATE_DOWN, &hdev->state);
 
-       if (IS_ENABLED(CONFIG_PCI_IOV))
-               hclge_disable_sriov(hdev);
-
        if (hdev->service_timer.function)
                del_timer_sync(&hdev->service_timer);
        if (hdev->service_task.func)
@@ -6287,7 +6253,9 @@ static int hclge_init(void)
 {
        pr_info("%s is initializing\n", HCLGE_NAME);
 
-       return hnae3_register_ae_algo(&ae_algo);
+       hnae3_register_ae_algo(&ae_algo);
+
+       return 0;
 }
 
 static void hclge_exit(void)
index af736a4..93177d9 100644 (file)
@@ -17,7 +17,7 @@
 #include "hclge_cmd.h"
 #include "hnae3.h"
 
-#define HCLGE_MOD_VERSION "v1.0"
+#define HCLGE_MOD_VERSION "1.0"
 #define HCLGE_DRIVER_NAME "hclge"
 
 #define HCLGE_INVALID_VPORT 0xffff
index c69ecab..262c125 100644 (file)
@@ -500,7 +500,8 @@ static int hclge_tm_qs_schd_mode_cfg(struct hclge_dev *hdev, u16 qs_id, u8 mode)
        return hclge_cmd_send(&hdev->hw, &desc, 1);
 }
 
-static int hclge_tm_qs_bp_cfg(struct hclge_dev *hdev, u8 tc)
+static int hclge_tm_qs_bp_cfg(struct hclge_dev *hdev, u8 tc, u8 grp_id,
+                             u32 bit_map)
 {
        struct hclge_bp_to_qs_map_cmd *bp_to_qs_map_cmd;
        struct hclge_desc desc;
@@ -511,9 +512,8 @@ static int hclge_tm_qs_bp_cfg(struct hclge_dev *hdev, u8 tc)
        bp_to_qs_map_cmd = (struct hclge_bp_to_qs_map_cmd *)desc.data;
 
        bp_to_qs_map_cmd->tc_id = tc;
-
-       /* Qset and tc is one by one mapping */
-       bp_to_qs_map_cmd->qs_bit_map = cpu_to_le32(1 << tc);
+       bp_to_qs_map_cmd->qs_group_id = grp_id;
+       bp_to_qs_map_cmd->qs_bit_map = cpu_to_le32(bit_map);
 
        return hclge_cmd_send(&hdev->hw, &desc, 1);
 }
@@ -1167,6 +1167,41 @@ static int hclge_pfc_setup_hw(struct hclge_dev *hdev)
                                      hdev->tm_info.hw_pfc_map);
 }
 
+/* Each Tc has a 1024 queue sets to backpress, it divides to
+ * 32 group, each group contains 32 queue sets, which can be
+ * represented by u32 bitmap.
+ */
+static int hclge_bp_setup_hw(struct hclge_dev *hdev, u8 tc)
+{
+       struct hclge_vport *vport = hdev->vport;
+       u32 i, k, qs_bitmap;
+       int ret;
+
+       for (i = 0; i < HCLGE_BP_GRP_NUM; i++) {
+               qs_bitmap = 0;
+
+               for (k = 0; k < hdev->num_alloc_vport; k++) {
+                       u16 qs_id = vport->qs_offset + tc;
+                       u8 grp, sub_grp;
+
+                       grp = hnae_get_field(qs_id, HCLGE_BP_GRP_ID_M,
+                                            HCLGE_BP_GRP_ID_S);
+                       sub_grp = hnae_get_field(qs_id, HCLGE_BP_SUB_GRP_ID_M,
+                                                HCLGE_BP_SUB_GRP_ID_S);
+                       if (i == grp)
+                               qs_bitmap |= (1 << sub_grp);
+
+                       vport++;
+               }
+
+               ret = hclge_tm_qs_bp_cfg(hdev, tc, i, qs_bitmap);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static int hclge_mac_pause_setup_hw(struct hclge_dev *hdev)
 {
        bool tx_en, rx_en;
@@ -1218,7 +1253,7 @@ int hclge_pause_setup_hw(struct hclge_dev *hdev)
                dev_warn(&hdev->pdev->dev, "set pfc pause failed:%d\n", ret);
 
        for (i = 0; i < hdev->tm_info.num_tc; i++) {
-               ret = hclge_tm_qs_bp_cfg(hdev, i);
+               ret = hclge_bp_setup_hw(hdev, i);
                if (ret)
                        return ret;
        }
index 2dbe177..c2b6e8a 100644 (file)
@@ -89,6 +89,11 @@ struct hclge_pg_shapping_cmd {
        __le32 pg_shapping_para;
 };
 
+#define HCLGE_BP_GRP_NUM               32
+#define HCLGE_BP_SUB_GRP_ID_S          0
+#define HCLGE_BP_SUB_GRP_ID_M          GENMASK(4, 0)
+#define HCLGE_BP_GRP_ID_S              5
+#define HCLGE_BP_GRP_ID_M              GENMASK(9, 5)
 struct hclge_bp_to_qs_map_cmd {
        u8 tc_id;
        u8 rsvd[2];
index 2dbffce..2b0e329 100644 (file)
@@ -1563,7 +1563,7 @@ static int hclgevf_pci_init(struct hclgevf_dev *hdev)
        ret = pci_enable_device(pdev);
        if (ret) {
                dev_err(&pdev->dev, "failed to enable PCI device\n");
-               goto err_no_drvdata;
+               return ret;
        }
 
        ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
@@ -1595,8 +1595,7 @@ err_clr_master:
        pci_release_regions(pdev);
 err_disable_device:
        pci_disable_device(pdev);
-err_no_drvdata:
-       pci_set_drvdata(pdev, NULL);
+
        return ret;
 }
 
@@ -1608,7 +1607,6 @@ static void hclgevf_pci_uninit(struct hclgevf_dev *hdev)
        pci_clear_master(pdev);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
@@ -1636,6 +1634,10 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
 
        hclgevf_state_init(hdev);
 
+       ret = hclgevf_cmd_init(hdev);
+       if (ret)
+               goto err_cmd_init;
+
        ret = hclgevf_misc_irq_init(hdev);
        if (ret) {
                dev_err(&pdev->dev, "failed(%d) to init Misc IRQ(vector0)\n",
@@ -1643,10 +1645,6 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
                goto err_misc_irq_init;
        }
 
-       ret = hclgevf_cmd_init(hdev);
-       if (ret)
-               goto err_cmd_init;
-
        ret = hclgevf_configure(hdev);
        if (ret) {
                dev_err(&pdev->dev, "failed(%d) to fetch configuration\n", ret);
@@ -1694,10 +1692,10 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
        return 0;
 
 err_config:
-       hclgevf_cmd_uninit(hdev);
-err_cmd_init:
        hclgevf_misc_irq_uninit(hdev);
 err_misc_irq_init:
+       hclgevf_cmd_uninit(hdev);
+err_cmd_init:
        hclgevf_state_uninit(hdev);
        hclgevf_uninit_msi(hdev);
 err_irq_init:
@@ -1707,9 +1705,9 @@ err_irq_init:
 
 static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev)
 {
-       hclgevf_cmd_uninit(hdev);
-       hclgevf_misc_irq_uninit(hdev);
        hclgevf_state_uninit(hdev);
+       hclgevf_misc_irq_uninit(hdev);
+       hclgevf_cmd_uninit(hdev);
        hclgevf_uninit_msi(hdev);
        hclgevf_pci_uninit(hdev);
 }
@@ -1854,7 +1852,9 @@ static int hclgevf_init(void)
 {
        pr_info("%s is initializing\n", HCLGEVF_NAME);
 
-       return hnae3_register_ae_algo(&ae_algovf);
+       hnae3_register_ae_algo(&ae_algovf);
+
+       return 0;
 }
 
 static void hclgevf_exit(void)
index a477a7c..9763e74 100644 (file)
@@ -9,7 +9,7 @@
 #include "hclgevf_cmd.h"
 #include "hnae3.h"
 
-#define HCLGEVF_MOD_VERSION "v1.0"
+#define HCLGEVF_MOD_VERSION "1.0"
 #define HCLGEVF_DRIVER_NAME "hclgevf"
 
 #define HCLGEVF_ROCEE_VECTOR_NUM       0
index 6e8d6a6..4bb4646 100644 (file)
@@ -192,6 +192,7 @@ static int alloc_long_term_buff(struct ibmvnic_adapter *adapter,
        if (adapter->fw_done_rc) {
                dev_err(dev, "Couldn't map long term buffer,rc = %d\n",
                        adapter->fw_done_rc);
+               dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr);
                return -1;
        }
        return 0;
@@ -1821,9 +1822,8 @@ static int do_reset(struct ibmvnic_adapter *adapter,
                        if (rc)
                                return rc;
                }
+               ibmvnic_disable_irqs(adapter);
        }
-
-       ibmvnic_disable_irqs(adapter);
        adapter->state = VNIC_CLOSED;
 
        if (reset_state == VNIC_CLOSED)
@@ -4586,14 +4586,6 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter)
                release_crq_queue(adapter);
        }
 
-       rc = init_stats_buffers(adapter);
-       if (rc)
-               return rc;
-
-       rc = init_stats_token(adapter);
-       if (rc)
-               return rc;
-
        return rc;
 }
 
@@ -4662,13 +4654,21 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
                        goto ibmvnic_init_fail;
        } while (rc == EAGAIN);
 
+       rc = init_stats_buffers(adapter);
+       if (rc)
+               goto ibmvnic_init_fail;
+
+       rc = init_stats_token(adapter);
+       if (rc)
+               goto ibmvnic_stats_fail;
+
        netdev->mtu = adapter->req_mtu - ETH_HLEN;
        netdev->min_mtu = adapter->min_mtu - ETH_HLEN;
        netdev->max_mtu = adapter->max_mtu - ETH_HLEN;
 
        rc = device_create_file(&dev->dev, &dev_attr_failover);
        if (rc)
-               goto ibmvnic_init_fail;
+               goto ibmvnic_dev_file_err;
 
        netif_carrier_off(netdev);
        rc = register_netdev(netdev);
@@ -4687,6 +4687,12 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
 ibmvnic_register_fail:
        device_remove_file(&dev->dev, &dev_attr_failover);
 
+ibmvnic_dev_file_err:
+       release_stats_token(adapter);
+
+ibmvnic_stats_fail:
+       release_stats_buffers(adapter);
+
 ibmvnic_init_fail:
        release_sub_crqs(adapter, 1);
        release_crq_queue(adapter);
index fc6a5ee..6947a2a 100644 (file)
@@ -19,13 +19,13 @@ struct i40e_stats {
 }
 
 #define I40E_NETDEV_STAT(_net_stat) \
-               I40E_STAT(struct rtnl_link_stats64, #_net_stat, _net_stat)
+       I40E_STAT(struct rtnl_link_stats64, #_net_stat, _net_stat)
 #define I40E_PF_STAT(_name, _stat) \
-               I40E_STAT(struct i40e_pf, _name, _stat)
+       I40E_STAT(struct i40e_pf, _name, _stat)
 #define I40E_VSI_STAT(_name, _stat) \
-               I40E_STAT(struct i40e_vsi, _name, _stat)
+       I40E_STAT(struct i40e_vsi, _name, _stat)
 #define I40E_VEB_STAT(_name, _stat) \
-               I40E_STAT(struct i40e_veb, _name, _stat)
+       I40E_STAT(struct i40e_veb, _name, _stat)
 
 static const struct i40e_stats i40e_gstrings_net_stats[] = {
        I40E_NETDEV_STAT(rx_packets),
@@ -42,18 +42,18 @@ static const struct i40e_stats i40e_gstrings_net_stats[] = {
 };
 
 static const struct i40e_stats i40e_gstrings_veb_stats[] = {
-       I40E_VEB_STAT("rx_bytes", stats.rx_bytes),
-       I40E_VEB_STAT("tx_bytes", stats.tx_bytes),
-       I40E_VEB_STAT("rx_unicast", stats.rx_unicast),
-       I40E_VEB_STAT("tx_unicast", stats.tx_unicast),
-       I40E_VEB_STAT("rx_multicast", stats.rx_multicast),
-       I40E_VEB_STAT("tx_multicast", stats.tx_multicast),
-       I40E_VEB_STAT("rx_broadcast", stats.rx_broadcast),
-       I40E_VEB_STAT("tx_broadcast", stats.tx_broadcast),
-       I40E_VEB_STAT("rx_discards", stats.rx_discards),
-       I40E_VEB_STAT("tx_discards", stats.tx_discards),
-       I40E_VEB_STAT("tx_errors", stats.tx_errors),
-       I40E_VEB_STAT("rx_unknown_protocol", stats.rx_unknown_protocol),
+       I40E_VEB_STAT("veb.rx_bytes", stats.rx_bytes),
+       I40E_VEB_STAT("veb.tx_bytes", stats.tx_bytes),
+       I40E_VEB_STAT("veb.rx_unicast", stats.rx_unicast),
+       I40E_VEB_STAT("veb.tx_unicast", stats.tx_unicast),
+       I40E_VEB_STAT("veb.rx_multicast", stats.rx_multicast),
+       I40E_VEB_STAT("veb.tx_multicast", stats.tx_multicast),
+       I40E_VEB_STAT("veb.rx_broadcast", stats.rx_broadcast),
+       I40E_VEB_STAT("veb.tx_broadcast", stats.tx_broadcast),
+       I40E_VEB_STAT("veb.rx_discards", stats.rx_discards),
+       I40E_VEB_STAT("veb.tx_discards", stats.tx_discards),
+       I40E_VEB_STAT("veb.tx_errors", stats.tx_errors),
+       I40E_VEB_STAT("veb.rx_unknown_protocol", stats.rx_unknown_protocol),
 };
 
 static const struct i40e_stats i40e_gstrings_misc_stats[] = {
@@ -66,6 +66,7 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = {
        I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol),
        I40E_VSI_STAT("tx_linearize", tx_linearize),
        I40E_VSI_STAT("tx_force_wb", tx_force_wb),
+       I40E_VSI_STAT("tx_busy", tx_busy),
        I40E_VSI_STAT("rx_alloc_fail", rx_buf_failed),
        I40E_VSI_STAT("rx_pg_alloc_fail", rx_page_failed),
 };
@@ -81,76 +82,77 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = {
  * is queried on the base PF netdev, not on the VMDq or FCoE netdev.
  */
 static const struct i40e_stats i40e_gstrings_stats[] = {
-       I40E_PF_STAT("rx_bytes", stats.eth.rx_bytes),
-       I40E_PF_STAT("tx_bytes", stats.eth.tx_bytes),
-       I40E_PF_STAT("rx_unicast", stats.eth.rx_unicast),
-       I40E_PF_STAT("tx_unicast", stats.eth.tx_unicast),
-       I40E_PF_STAT("rx_multicast", stats.eth.rx_multicast),
-       I40E_PF_STAT("tx_multicast", stats.eth.tx_multicast),
-       I40E_PF_STAT("rx_broadcast", stats.eth.rx_broadcast),
-       I40E_PF_STAT("tx_broadcast", stats.eth.tx_broadcast),
-       I40E_PF_STAT("tx_errors", stats.eth.tx_errors),
-       I40E_PF_STAT("rx_dropped", stats.eth.rx_discards),
-       I40E_PF_STAT("tx_dropped_link_down", stats.tx_dropped_link_down),
-       I40E_PF_STAT("rx_crc_errors", stats.crc_errors),
-       I40E_PF_STAT("illegal_bytes", stats.illegal_bytes),
-       I40E_PF_STAT("mac_local_faults", stats.mac_local_faults),
-       I40E_PF_STAT("mac_remote_faults", stats.mac_remote_faults),
-       I40E_PF_STAT("tx_timeout", tx_timeout_count),
-       I40E_PF_STAT("rx_csum_bad", hw_csum_rx_error),
-       I40E_PF_STAT("rx_length_errors", stats.rx_length_errors),
-       I40E_PF_STAT("link_xon_rx", stats.link_xon_rx),
-       I40E_PF_STAT("link_xoff_rx", stats.link_xoff_rx),
-       I40E_PF_STAT("link_xon_tx", stats.link_xon_tx),
-       I40E_PF_STAT("link_xoff_tx", stats.link_xoff_tx),
-       I40E_PF_STAT("priority_xon_rx", stats.priority_xon_rx),
-       I40E_PF_STAT("priority_xoff_rx", stats.priority_xoff_rx),
-       I40E_PF_STAT("priority_xon_tx", stats.priority_xon_tx),
-       I40E_PF_STAT("priority_xoff_tx", stats.priority_xoff_tx),
-       I40E_PF_STAT("rx_size_64", stats.rx_size_64),
-       I40E_PF_STAT("rx_size_127", stats.rx_size_127),
-       I40E_PF_STAT("rx_size_255", stats.rx_size_255),
-       I40E_PF_STAT("rx_size_511", stats.rx_size_511),
-       I40E_PF_STAT("rx_size_1023", stats.rx_size_1023),
-       I40E_PF_STAT("rx_size_1522", stats.rx_size_1522),
-       I40E_PF_STAT("rx_size_big", stats.rx_size_big),
-       I40E_PF_STAT("tx_size_64", stats.tx_size_64),
-       I40E_PF_STAT("tx_size_127", stats.tx_size_127),
-       I40E_PF_STAT("tx_size_255", stats.tx_size_255),
-       I40E_PF_STAT("tx_size_511", stats.tx_size_511),
-       I40E_PF_STAT("tx_size_1023", stats.tx_size_1023),
-       I40E_PF_STAT("tx_size_1522", stats.tx_size_1522),
-       I40E_PF_STAT("tx_size_big", stats.tx_size_big),
-       I40E_PF_STAT("rx_undersize", stats.rx_undersize),
-       I40E_PF_STAT("rx_fragments", stats.rx_fragments),
-       I40E_PF_STAT("rx_oversize", stats.rx_oversize),
-       I40E_PF_STAT("rx_jabber", stats.rx_jabber),
-       I40E_PF_STAT("VF_admin_queue_requests", vf_aq_requests),
-       I40E_PF_STAT("arq_overflows", arq_overflows),
-       I40E_PF_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
-       I40E_PF_STAT("tx_hwtstamp_skipped", tx_hwtstamp_skipped),
-       I40E_PF_STAT("fdir_flush_cnt", fd_flush_cnt),
-       I40E_PF_STAT("fdir_atr_match", stats.fd_atr_match),
-       I40E_PF_STAT("fdir_atr_tunnel_match", stats.fd_atr_tunnel_match),
-       I40E_PF_STAT("fdir_atr_status", stats.fd_atr_status),
-       I40E_PF_STAT("fdir_sb_match", stats.fd_sb_match),
-       I40E_PF_STAT("fdir_sb_status", stats.fd_sb_status),
+       I40E_PF_STAT("port.rx_bytes", stats.eth.rx_bytes),
+       I40E_PF_STAT("port.tx_bytes", stats.eth.tx_bytes),
+       I40E_PF_STAT("port.rx_unicast", stats.eth.rx_unicast),
+       I40E_PF_STAT("port.tx_unicast", stats.eth.tx_unicast),
+       I40E_PF_STAT("port.rx_multicast", stats.eth.rx_multicast),
+       I40E_PF_STAT("port.tx_multicast", stats.eth.tx_multicast),
+       I40E_PF_STAT("port.rx_broadcast", stats.eth.rx_broadcast),
+       I40E_PF_STAT("port.tx_broadcast", stats.eth.tx_broadcast),
+       I40E_PF_STAT("port.tx_errors", stats.eth.tx_errors),
+       I40E_PF_STAT("port.rx_dropped", stats.eth.rx_discards),
+       I40E_PF_STAT("port.tx_dropped_link_down", stats.tx_dropped_link_down),
+       I40E_PF_STAT("port.rx_crc_errors", stats.crc_errors),
+       I40E_PF_STAT("port.illegal_bytes", stats.illegal_bytes),
+       I40E_PF_STAT("port.mac_local_faults", stats.mac_local_faults),
+       I40E_PF_STAT("port.mac_remote_faults", stats.mac_remote_faults),
+       I40E_PF_STAT("port.tx_timeout", tx_timeout_count),
+       I40E_PF_STAT("port.rx_csum_bad", hw_csum_rx_error),
+       I40E_PF_STAT("port.rx_length_errors", stats.rx_length_errors),
+       I40E_PF_STAT("port.link_xon_rx", stats.link_xon_rx),
+       I40E_PF_STAT("port.link_xoff_rx", stats.link_xoff_rx),
+       I40E_PF_STAT("port.link_xon_tx", stats.link_xon_tx),
+       I40E_PF_STAT("port.link_xoff_tx", stats.link_xoff_tx),
+       I40E_PF_STAT("port.rx_size_64", stats.rx_size_64),
+       I40E_PF_STAT("port.rx_size_127", stats.rx_size_127),
+       I40E_PF_STAT("port.rx_size_255", stats.rx_size_255),
+       I40E_PF_STAT("port.rx_size_511", stats.rx_size_511),
+       I40E_PF_STAT("port.rx_size_1023", stats.rx_size_1023),
+       I40E_PF_STAT("port.rx_size_1522", stats.rx_size_1522),
+       I40E_PF_STAT("port.rx_size_big", stats.rx_size_big),
+       I40E_PF_STAT("port.tx_size_64", stats.tx_size_64),
+       I40E_PF_STAT("port.tx_size_127", stats.tx_size_127),
+       I40E_PF_STAT("port.tx_size_255", stats.tx_size_255),
+       I40E_PF_STAT("port.tx_size_511", stats.tx_size_511),
+       I40E_PF_STAT("port.tx_size_1023", stats.tx_size_1023),
+       I40E_PF_STAT("port.tx_size_1522", stats.tx_size_1522),
+       I40E_PF_STAT("port.tx_size_big", stats.tx_size_big),
+       I40E_PF_STAT("port.rx_undersize", stats.rx_undersize),
+       I40E_PF_STAT("port.rx_fragments", stats.rx_fragments),
+       I40E_PF_STAT("port.rx_oversize", stats.rx_oversize),
+       I40E_PF_STAT("port.rx_jabber", stats.rx_jabber),
+       I40E_PF_STAT("port.VF_admin_queue_requests", vf_aq_requests),
+       I40E_PF_STAT("port.arq_overflows", arq_overflows),
+       I40E_PF_STAT("port.tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
+       I40E_PF_STAT("port.rx_hwtstamp_cleared", rx_hwtstamp_cleared),
+       I40E_PF_STAT("port.tx_hwtstamp_skipped", tx_hwtstamp_skipped),
+       I40E_PF_STAT("port.fdir_flush_cnt", fd_flush_cnt),
+       I40E_PF_STAT("port.fdir_atr_match", stats.fd_atr_match),
+       I40E_PF_STAT("port.fdir_atr_tunnel_match", stats.fd_atr_tunnel_match),
+       I40E_PF_STAT("port.fdir_atr_status", stats.fd_atr_status),
+       I40E_PF_STAT("port.fdir_sb_match", stats.fd_sb_match),
+       I40E_PF_STAT("port.fdir_sb_status", stats.fd_sb_status),
 
        /* LPI stats */
-       I40E_PF_STAT("tx_lpi_status", stats.tx_lpi_status),
-       I40E_PF_STAT("rx_lpi_status", stats.rx_lpi_status),
-       I40E_PF_STAT("tx_lpi_count", stats.tx_lpi_count),
-       I40E_PF_STAT("rx_lpi_count", stats.rx_lpi_count),
+       I40E_PF_STAT("port.tx_lpi_status", stats.tx_lpi_status),
+       I40E_PF_STAT("port.rx_lpi_status", stats.rx_lpi_status),
+       I40E_PF_STAT("port.tx_lpi_count", stats.tx_lpi_count),
+       I40E_PF_STAT("port.rx_lpi_count", stats.rx_lpi_count),
 };
 
-#define I40E_QUEUE_STATS_LEN(n) \
-       (((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs \
+/* We use num_tx_queues here as a proxy for the maximum number of queues
+ * available because we always allocate queues symmetrically.
+ */
+#define I40E_MAX_NUM_QUEUES(n) ((n)->num_tx_queues)
+#define I40E_QUEUE_STATS_LEN(n)                                              \
+          (I40E_MAX_NUM_QUEUES(n)                                           \
            * 2 /* Tx and Rx together */                                     \
            * (sizeof(struct i40e_queue_stats) / sizeof(u64)))
 #define I40E_GLOBAL_STATS_LEN  ARRAY_SIZE(i40e_gstrings_stats)
-#define I40E_NETDEV_STATS_LEN   ARRAY_SIZE(i40e_gstrings_net_stats)
+#define I40E_NETDEV_STATS_LEN  ARRAY_SIZE(i40e_gstrings_net_stats)
 #define I40E_MISC_STATS_LEN    ARRAY_SIZE(i40e_gstrings_misc_stats)
-#define I40E_VSI_STATS_LEN(n)   (I40E_NETDEV_STATS_LEN + \
+#define I40E_VSI_STATS_LEN(n)  (I40E_NETDEV_STATS_LEN + \
                                 I40E_MISC_STATS_LEN + \
                                 I40E_QUEUE_STATS_LEN((n)))
 #define I40E_PFC_STATS_LEN ( \
@@ -1658,6 +1660,32 @@ done:
        return err;
 }
 
+/**
+ * i40e_get_stats_count - return the stats count for a device
+ * @netdev: the netdev to return the count for
+ *
+ * Returns the total number of statistics for this netdev. Note that even
+ * though this is a function, it is required that the count for a specific
+ * netdev must never change. Basing the count on static values such as the
+ * maximum number of queues or the device type is ok. However, the API for
+ * obtaining stats is *not* safe against changes based on non-static
+ * values such as the *current* number of queues, or runtime flags.
+ *
+ * If a statistic is not always enabled, return it as part of the count
+ * anyways, always return its string, and report its value as zero.
+ **/
+static int i40e_get_stats_count(struct net_device *netdev)
+{
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_vsi *vsi = np->vsi;
+       struct i40e_pf *pf = vsi->back;
+
+       if (vsi == pf->vsi[pf->lan_vsi] && pf->hw.partition_id == 1)
+               return I40E_PF_STATS_LEN(netdev) + I40E_VEB_STATS_TOTAL;
+       else
+               return I40E_VSI_STATS_LEN(netdev);
+}
+
 static int i40e_get_sset_count(struct net_device *netdev, int sset)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
@@ -1668,16 +1696,7 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset)
        case ETH_SS_TEST:
                return I40E_TEST_LEN;
        case ETH_SS_STATS:
-               if (vsi == pf->vsi[pf->lan_vsi] && pf->hw.partition_id == 1) {
-                       int len = I40E_PF_STATS_LEN(netdev);
-
-                       if ((pf->lan_veb != I40E_NO_VEB) &&
-                           (pf->flags & I40E_FLAG_VEB_STATS_ENABLED))
-                               len += I40E_VEB_STATS_TOTAL;
-                       return len;
-               } else {
-                       return I40E_VSI_STATS_LEN(netdev);
-               }
+               return i40e_get_stats_count(netdev);
        case ETH_SS_PRIV_FLAGS:
                return I40E_PRIV_FLAGS_STR_LEN +
                        (pf->hw.pf_id == 0 ? I40E_GL_PRIV_FLAGS_STR_LEN : 0);
@@ -1686,6 +1705,20 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset)
        }
 }
 
+/**
+ * i40e_get_ethtool_stats - copy stat values into supplied buffer
+ * @netdev: the netdev to collect stats for
+ * @stats: ethtool stats command structure
+ * @data: ethtool supplied buffer
+ *
+ * Copy the stats values for this netdev into the buffer. Expects data to be
+ * pre-allocated to the size returned by i40e_get_stats_count.. Note that all
+ * statistics must be copied in a static order, and the count must not change
+ * for a given netdev. See i40e_get_stats_count for more details.
+ *
+ * If a statistic is not currently valid (such as a disabled queue), this
+ * function reports its value as zero.
+ **/
 static void i40e_get_ethtool_stats(struct net_device *netdev,
                                   struct ethtool_stats *stats, u64 *data)
 {
@@ -1693,47 +1726,54 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
        struct i40e_ring *tx_ring, *rx_ring;
        struct i40e_vsi *vsi = np->vsi;
        struct i40e_pf *pf = vsi->back;
-       unsigned int j;
-       int i = 0;
+       unsigned int i;
        char *p;
        struct rtnl_link_stats64 *net_stats = i40e_get_vsi_stats_struct(vsi);
        unsigned int start;
 
        i40e_update_stats(vsi);
 
-       for (j = 0; j < I40E_NETDEV_STATS_LEN; j++) {
-               p = (char *)net_stats + i40e_gstrings_net_stats[j].stat_offset;
-               data[i++] = (i40e_gstrings_net_stats[j].sizeof_stat ==
+       for (i = 0; i < I40E_NETDEV_STATS_LEN; i++) {
+               p = (char *)net_stats + i40e_gstrings_net_stats[i].stat_offset;
+               *(data++) = (i40e_gstrings_net_stats[i].sizeof_stat ==
                        sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
        }
-       for (j = 0; j < I40E_MISC_STATS_LEN; j++) {
-               p = (char *)vsi + i40e_gstrings_misc_stats[j].stat_offset;
-               data[i++] = (i40e_gstrings_misc_stats[j].sizeof_stat ==
+       for (i = 0; i < I40E_MISC_STATS_LEN; i++) {
+               p = (char *)vsi + i40e_gstrings_misc_stats[i].stat_offset;
+               *(data++) = (i40e_gstrings_misc_stats[i].sizeof_stat ==
                            sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
        }
        rcu_read_lock();
-       for (j = 0; j < vsi->num_queue_pairs; j++) {
-               tx_ring = READ_ONCE(vsi->tx_rings[j]);
+       for (i = 0; i < I40E_MAX_NUM_QUEUES(netdev) ; i++) {
+               tx_ring = READ_ONCE(vsi->tx_rings[i]);
 
-               if (!tx_ring)
+               if (!tx_ring) {
+                       /* Bump the stat counter to skip these stats, and make
+                        * sure the memory is zero'd
+                        */
+                       *(data++) = 0;
+                       *(data++) = 0;
+                       *(data++) = 0;
+                       *(data++) = 0;
                        continue;
+               }
 
                /* process Tx ring statistics */
                do {
                        start = u64_stats_fetch_begin_irq(&tx_ring->syncp);
-                       data[i] = tx_ring->stats.packets;
-                       data[i + 1] = tx_ring->stats.bytes;
+                       data[0] = tx_ring->stats.packets;
+                       data[1] = tx_ring->stats.bytes;
                } while (u64_stats_fetch_retry_irq(&tx_ring->syncp, start));
-               i += 2;
+               data += 2;
 
                /* Rx ring is the 2nd half of the queue pair */
                rx_ring = &tx_ring[1];
                do {
                        start = u64_stats_fetch_begin_irq(&rx_ring->syncp);
-                       data[i] = rx_ring->stats.packets;
-                       data[i + 1] = rx_ring->stats.bytes;
+                       data[0] = rx_ring->stats.packets;
+                       data[1] = rx_ring->stats.bytes;
                } while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start));
-               i += 2;
+               data += 2;
        }
        rcu_read_unlock();
        if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1)
@@ -1743,38 +1783,131 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
            (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) {
                struct i40e_veb *veb = pf->veb[pf->lan_veb];
 
-               for (j = 0; j < I40E_VEB_STATS_LEN; j++) {
+               for (i = 0; i < I40E_VEB_STATS_LEN; i++) {
                        p = (char *)veb;
-                       p += i40e_gstrings_veb_stats[j].stat_offset;
-                       data[i++] = (i40e_gstrings_veb_stats[j].sizeof_stat ==
+                       p += i40e_gstrings_veb_stats[i].stat_offset;
+                       *(data++) = (i40e_gstrings_veb_stats[i].sizeof_stat ==
                                     sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
                }
-               for (j = 0; j < I40E_MAX_TRAFFIC_CLASS; j++) {
-                       data[i++] = veb->tc_stats.tc_tx_packets[j];
-                       data[i++] = veb->tc_stats.tc_tx_bytes[j];
-                       data[i++] = veb->tc_stats.tc_rx_packets[j];
-                       data[i++] = veb->tc_stats.tc_rx_bytes[j];
+               for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+                       *(data++) = veb->tc_stats.tc_tx_packets[i];
+                       *(data++) = veb->tc_stats.tc_tx_bytes[i];
+                       *(data++) = veb->tc_stats.tc_rx_packets[i];
+                       *(data++) = veb->tc_stats.tc_rx_bytes[i];
                }
+       } else {
+               data += I40E_VEB_STATS_TOTAL;
        }
-       for (j = 0; j < I40E_GLOBAL_STATS_LEN; j++) {
-               p = (char *)pf + i40e_gstrings_stats[j].stat_offset;
-               data[i++] = (i40e_gstrings_stats[j].sizeof_stat ==
+       for (i = 0; i < I40E_GLOBAL_STATS_LEN; i++) {
+               p = (char *)pf + i40e_gstrings_stats[i].stat_offset;
+               *(data++) = (i40e_gstrings_stats[i].sizeof_stat ==
                             sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
        }
-       for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
-               data[i++] = pf->stats.priority_xon_tx[j];
-               data[i++] = pf->stats.priority_xoff_tx[j];
+       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+               *(data++) = pf->stats.priority_xon_tx[i];
+               *(data++) = pf->stats.priority_xoff_tx[i];
        }
-       for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
-               data[i++] = pf->stats.priority_xon_rx[j];
-               data[i++] = pf->stats.priority_xoff_rx[j];
+       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+               *(data++) = pf->stats.priority_xon_rx[i];
+               *(data++) = pf->stats.priority_xoff_rx[i];
        }
-       for (j = 0; j < I40E_MAX_USER_PRIORITY; j++)
-               data[i++] = pf->stats.priority_xon_2_xoff[j];
+       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++)
+               *(data++) = pf->stats.priority_xon_2_xoff[i];
 }
 
-static void i40e_get_strings(struct net_device *netdev, u32 stringset,
-                            u8 *data)
+/**
+ * i40e_get_stat_strings - copy stat strings into supplied buffer
+ * @netdev: the netdev to collect strings for
+ * @data: supplied buffer to copy strings into
+ *
+ * Copy the strings related to stats for this netdev. Expects data to be
+ * pre-allocated with the size reported by i40e_get_stats_count. Note that the
+ * strings must be copied in a static order and the total count must not
+ * change for a given netdev. See i40e_get_stats_count for more details.
+ **/
+static void i40e_get_stat_strings(struct net_device *netdev, u8 *data)
+{
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_vsi *vsi = np->vsi;
+       struct i40e_pf *pf = vsi->back;
+       unsigned int i;
+       u8 *p = data;
+
+       for (i = 0; i < I40E_NETDEV_STATS_LEN; i++) {
+               snprintf(data, ETH_GSTRING_LEN, "%s",
+                        i40e_gstrings_net_stats[i].stat_string);
+               data += ETH_GSTRING_LEN;
+       }
+       for (i = 0; i < I40E_MISC_STATS_LEN; i++) {
+               snprintf(data, ETH_GSTRING_LEN, "%s",
+                        i40e_gstrings_misc_stats[i].stat_string);
+               data += ETH_GSTRING_LEN;
+       }
+       for (i = 0; i < I40E_MAX_NUM_QUEUES(netdev); i++) {
+               snprintf(data, ETH_GSTRING_LEN, "tx-%u.tx_packets", i);
+               data += ETH_GSTRING_LEN;
+               snprintf(data, ETH_GSTRING_LEN, "tx-%u.tx_bytes", i);
+               data += ETH_GSTRING_LEN;
+               snprintf(data, ETH_GSTRING_LEN, "rx-%u.rx_packets", i);
+               data += ETH_GSTRING_LEN;
+               snprintf(data, ETH_GSTRING_LEN, "rx-%u.rx_bytes", i);
+               data += ETH_GSTRING_LEN;
+       }
+       if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1)
+               return;
+
+       for (i = 0; i < I40E_VEB_STATS_LEN; i++) {
+               snprintf(data, ETH_GSTRING_LEN, "%s",
+                        i40e_gstrings_veb_stats[i].stat_string);
+               data += ETH_GSTRING_LEN;
+       }
+       for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+               snprintf(data, ETH_GSTRING_LEN,
+                        "veb.tc_%u_tx_packets", i);
+               data += ETH_GSTRING_LEN;
+               snprintf(data, ETH_GSTRING_LEN,
+                        "veb.tc_%u_tx_bytes", i);
+               data += ETH_GSTRING_LEN;
+               snprintf(data, ETH_GSTRING_LEN,
+                        "veb.tc_%u_rx_packets", i);
+               data += ETH_GSTRING_LEN;
+               snprintf(data, ETH_GSTRING_LEN,
+                        "veb.tc_%u_rx_bytes", i);
+               data += ETH_GSTRING_LEN;
+       }
+
+       for (i = 0; i < I40E_GLOBAL_STATS_LEN; i++) {
+               snprintf(data, ETH_GSTRING_LEN, "%s",
+                        i40e_gstrings_stats[i].stat_string);
+               data += ETH_GSTRING_LEN;
+       }
+       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+               snprintf(data, ETH_GSTRING_LEN,
+                        "port.tx_priority_%u_xon", i);
+               data += ETH_GSTRING_LEN;
+               snprintf(data, ETH_GSTRING_LEN,
+                        "port.tx_priority_%u_xoff", i);
+               data += ETH_GSTRING_LEN;
+       }
+       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+               snprintf(data, ETH_GSTRING_LEN,
+                        "port.rx_priority_%u_xon", i);
+               data += ETH_GSTRING_LEN;
+               snprintf(data, ETH_GSTRING_LEN,
+                        "port.rx_priority_%u_xoff", i);
+               data += ETH_GSTRING_LEN;
+       }
+       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+               snprintf(data, ETH_GSTRING_LEN,
+                        "port.rx_priority_%u_xon_2_xoff", i);
+               data += ETH_GSTRING_LEN;
+       }
+
+       WARN_ONCE(p - data != i40e_get_stats_count(netdev) * ETH_GSTRING_LEN,
+                 "stat strings count mismatch!");
+}
+
+static void i40e_get_priv_flag_strings(struct net_device *netdev, u8 *data)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
@@ -1782,98 +1915,33 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset,
        char *p = (char *)data;
        unsigned int i;
 
+       for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) {
+               snprintf(p, ETH_GSTRING_LEN, "%s",
+                        i40e_gstrings_priv_flags[i].flag_string);
+               p += ETH_GSTRING_LEN;
+       }
+       if (pf->hw.pf_id != 0)
+               return;
+       for (i = 0; i < I40E_GL_PRIV_FLAGS_STR_LEN; i++) {
+               snprintf(p, ETH_GSTRING_LEN, "%s",
+                        i40e_gl_gstrings_priv_flags[i].flag_string);
+               p += ETH_GSTRING_LEN;
+       }
+}
+
+static void i40e_get_strings(struct net_device *netdev, u32 stringset,
+                            u8 *data)
+{
        switch (stringset) {
        case ETH_SS_TEST:
                memcpy(data, i40e_gstrings_test,
                       I40E_TEST_LEN * ETH_GSTRING_LEN);
                break;
        case ETH_SS_STATS:
-               for (i = 0; i < I40E_NETDEV_STATS_LEN; i++) {
-                       snprintf(p, ETH_GSTRING_LEN, "%s",
-                                i40e_gstrings_net_stats[i].stat_string);
-                       p += ETH_GSTRING_LEN;
-               }
-               for (i = 0; i < I40E_MISC_STATS_LEN; i++) {
-                       snprintf(p, ETH_GSTRING_LEN, "%s",
-                                i40e_gstrings_misc_stats[i].stat_string);
-                       p += ETH_GSTRING_LEN;
-               }
-               for (i = 0; i < vsi->num_queue_pairs; i++) {
-                       snprintf(p, ETH_GSTRING_LEN, "tx-%d.tx_packets", i);
-                       p += ETH_GSTRING_LEN;
-                       snprintf(p, ETH_GSTRING_LEN, "tx-%d.tx_bytes", i);
-                       p += ETH_GSTRING_LEN;
-                       snprintf(p, ETH_GSTRING_LEN, "rx-%d.rx_packets", i);
-                       p += ETH_GSTRING_LEN;
-                       snprintf(p, ETH_GSTRING_LEN, "rx-%d.rx_bytes", i);
-                       p += ETH_GSTRING_LEN;
-               }
-               if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1)
-                       return;
-
-               if ((pf->lan_veb != I40E_NO_VEB) &&
-                   (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) {
-                       for (i = 0; i < I40E_VEB_STATS_LEN; i++) {
-                               snprintf(p, ETH_GSTRING_LEN, "veb.%s",
-                                       i40e_gstrings_veb_stats[i].stat_string);
-                               p += ETH_GSTRING_LEN;
-                       }
-                       for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "veb.tc_%d_tx_packets", i);
-                               p += ETH_GSTRING_LEN;
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "veb.tc_%d_tx_bytes", i);
-                               p += ETH_GSTRING_LEN;
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "veb.tc_%d_rx_packets", i);
-                               p += ETH_GSTRING_LEN;
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "veb.tc_%d_rx_bytes", i);
-                               p += ETH_GSTRING_LEN;
-                       }
-               }
-               for (i = 0; i < I40E_GLOBAL_STATS_LEN; i++) {
-                       snprintf(p, ETH_GSTRING_LEN, "port.%s",
-                                i40e_gstrings_stats[i].stat_string);
-                       p += ETH_GSTRING_LEN;
-               }
-               for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
-                       snprintf(p, ETH_GSTRING_LEN,
-                                "port.tx_priority_%d_xon", i);
-                       p += ETH_GSTRING_LEN;
-                       snprintf(p, ETH_GSTRING_LEN,
-                                "port.tx_priority_%d_xoff", i);
-                       p += ETH_GSTRING_LEN;
-               }
-               for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
-                       snprintf(p, ETH_GSTRING_LEN,
-                                "port.rx_priority_%d_xon", i);
-                       p += ETH_GSTRING_LEN;
-                       snprintf(p, ETH_GSTRING_LEN,
-                                "port.rx_priority_%d_xoff", i);
-                       p += ETH_GSTRING_LEN;
-               }
-               for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
-                       snprintf(p, ETH_GSTRING_LEN,
-                                "port.rx_priority_%d_xon_2_xoff", i);
-                       p += ETH_GSTRING_LEN;
-               }
-               /* BUG_ON(p - data != I40E_STATS_LEN * ETH_GSTRING_LEN); */
+               i40e_get_stat_strings(netdev, data);
                break;
        case ETH_SS_PRIV_FLAGS:
-               for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) {
-                       snprintf(p, ETH_GSTRING_LEN, "%s",
-                                i40e_gstrings_priv_flags[i].flag_string);
-                       p += ETH_GSTRING_LEN;
-               }
-               if (pf->hw.pf_id != 0)
-                       break;
-               for (i = 0; i < I40E_GL_PRIV_FLAGS_STR_LEN; i++) {
-                       snprintf(p, ETH_GSTRING_LEN, "%s",
-                                i40e_gl_gstrings_priv_flags[i].flag_string);
-                       p += ETH_GSTRING_LEN;
-               }
+               i40e_get_priv_flag_strings(netdev, data);
                break;
        default:
                break;
index c8659fb..b5daa5c 100644 (file)
@@ -2840,6 +2840,23 @@ static int i40e_vlan_rx_add_vid(struct net_device *netdev,
        return ret;
 }
 
+/**
+ * i40e_vlan_rx_add_vid_up - Add a vlan id filter to HW offload in UP path
+ * @netdev: network interface to be adjusted
+ * @proto: unused protocol value
+ * @vid: vlan id to be added
+ **/
+static void i40e_vlan_rx_add_vid_up(struct net_device *netdev,
+                                   __always_unused __be16 proto, u16 vid)
+{
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_vsi *vsi = np->vsi;
+
+       if (vid >= VLAN_N_VID)
+               return;
+       set_bit(vid, vsi->active_vlans);
+}
+
 /**
  * i40e_vlan_rx_kill_vid - Remove a vlan id filter from HW offload
  * @netdev: network interface to be adjusted
@@ -2882,8 +2899,8 @@ static void i40e_restore_vlan(struct i40e_vsi *vsi)
                i40e_vlan_stripping_disable(vsi);
 
        for_each_set_bit(vid, vsi->active_vlans, VLAN_N_VID)
-               i40e_vlan_rx_add_vid(vsi->netdev, htons(ETH_P_8021Q),
-                                    vid);
+               i40e_vlan_rx_add_vid_up(vsi->netdev, htons(ETH_P_8021Q),
+                                       vid);
 }
 
 /**
@@ -10309,21 +10326,28 @@ static int i40e_init_msix(struct i40e_pf *pf)
 
        /* any vectors left over go for VMDq support */
        if (pf->flags & I40E_FLAG_VMDQ_ENABLED) {
-               int vmdq_vecs_wanted = pf->num_vmdq_vsis * pf->num_vmdq_qps;
-               int vmdq_vecs = min_t(int, vectors_left, vmdq_vecs_wanted);
-
                if (!vectors_left) {
                        pf->num_vmdq_msix = 0;
                        pf->num_vmdq_qps = 0;
                } else {
+                       int vmdq_vecs_wanted =
+                               pf->num_vmdq_vsis * pf->num_vmdq_qps;
+                       int vmdq_vecs =
+                               min_t(int, vectors_left, vmdq_vecs_wanted);
+
                        /* if we're short on vectors for what's desired, we limit
                         * the queues per vmdq.  If this is still more than are
                         * available, the user will need to change the number of
                         * queues/vectors used by the PF later with the ethtool
                         * channels command
                         */
-                       if (vmdq_vecs < vmdq_vecs_wanted)
+                       if (vectors_left < vmdq_vecs_wanted) {
                                pf->num_vmdq_qps = 1;
+                               vmdq_vecs_wanted = pf->num_vmdq_vsis;
+                               vmdq_vecs = min_t(int,
+                                                 vectors_left,
+                                                 vmdq_vecs_wanted);
+                       }
                        pf->num_vmdq_msix = pf->num_vmdq_qps;
 
                        v_budget += vmdq_vecs;
index aa3daec..35f2866 100644 (file)
@@ -317,10 +317,12 @@ void i40e_ptp_rx_hang(struct i40e_pf *pf)
  * This watchdog task is run periodically to make sure that we clear the Tx
  * timestamp logic if we don't obtain a timestamp in a reasonable amount of
  * time. It is unexpected in the normal case but if it occurs it results in
- * permanently prevent timestamps of future packets
+ * permanently preventing timestamps of future packets.
  **/
 void i40e_ptp_tx_hang(struct i40e_pf *pf)
 {
+       struct sk_buff *skb;
+
        if (!(pf->flags & I40E_FLAG_PTP) || !pf->ptp_tx)
                return;
 
@@ -333,9 +335,12 @@ void i40e_ptp_tx_hang(struct i40e_pf *pf)
         * within a second it is reasonable to assume that we never will.
         */
        if (time_is_before_jiffies(pf->ptp_tx_start + HZ)) {
-               dev_kfree_skb_any(pf->ptp_tx_skb);
+               skb = pf->ptp_tx_skb;
                pf->ptp_tx_skb = NULL;
                clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state);
+
+               /* Free the skb after we clear the bitlock */
+               dev_kfree_skb_any(skb);
                pf->tx_hwtstamp_timeouts++;
        }
 }
@@ -794,9 +799,11 @@ void i40e_ptp_stop(struct i40e_pf *pf)
        pf->ptp_rx = false;
 
        if (pf->ptp_tx_skb) {
-               dev_kfree_skb_any(pf->ptp_tx_skb);
+               struct sk_buff *skb = pf->ptp_tx_skb;
+
                pf->ptp_tx_skb = NULL;
                clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state);
+               dev_kfree_skb_any(skb);
        }
 
        if (pf->ptp_clock) {
index 98b8349..96e537a 100644 (file)
@@ -81,7 +81,6 @@ struct i40e_vsi {
 #define I40E_TX_DESC(R, i) (&(((struct i40e_tx_desc *)((R)->desc))[i]))
 #define I40E_TX_CTXTDESC(R, i) \
        (&(((struct i40e_tx_context_desc *)((R)->desc))[i]))
-#define MAX_QUEUES 16
 #define I40EVF_MAX_REQ_QUEUES 4
 
 #define I40EVF_HKEY_ARRAY_SIZE ((I40E_VFQF_HKEY_MAX_INDEX + 1) * 4)
index fc6592c..5585f36 100644 (file)
@@ -1,8 +1,8 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /* Copyright(c) 2013 - 2018 Intel Corporation. */
 
-#ifndef _I40E_CLIENT_H_
-#define _I40E_CLIENT_H_
+#ifndef _I40EVF_CLIENT_H_
+#define _I40EVF_CLIENT_H_
 
 #define I40EVF_CLIENT_STR_LENGTH 10
 
@@ -166,4 +166,4 @@ struct i40e_client {
 /* used by clients */
 int i40evf_register_client(struct i40e_client *client);
 int i40evf_unregister_client(struct i40e_client *client);
-#endif /* _I40E_CLIENT_H_ */
+#endif /* _I40EVF_CLIENT_H_ */
index 3f04a18..a7b87f9 100644 (file)
@@ -1925,7 +1925,8 @@ continue_reset:
         * ndo_open() returning, so we can't assume it means all our open
         * tasks have finished, since we're not holding the rtnl_lock here.
         */
-       running = (adapter->state == __I40EVF_RUNNING);
+       running = ((adapter->state == __I40EVF_RUNNING) ||
+                  (adapter->state == __I40EVF_RESETTING));
 
        if (running) {
                netif_carrier_off(netdev);
@@ -2331,7 +2332,7 @@ static int i40evf_validate_ch_config(struct i40evf_adapter *adapter,
                total_max_rate += tx_rate;
                num_qps += mqprio_qopt->qopt.count[i];
        }
-       if (num_qps > MAX_QUEUES)
+       if (num_qps > I40EVF_MAX_REQ_QUEUES)
                return -EINVAL;
 
        ret = i40evf_validate_tx_bandwidth(adapter, total_max_rate);
@@ -3689,7 +3690,8 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        pci_set_master(pdev);
 
-       netdev = alloc_etherdev_mq(sizeof(struct i40evf_adapter), MAX_QUEUES);
+       netdev = alloc_etherdev_mq(sizeof(struct i40evf_adapter),
+                                  I40EVF_MAX_REQ_QUEUES);
        if (!netdev) {
                err = -ENOMEM;
                goto err_alloc_etherdev;
index 7dc5f04..7541ec2 100644 (file)
@@ -1049,7 +1049,9 @@ struct ice_aqc_set_event_mask {
  * NVM Update commands (indirect 0x0703)
  */
 struct ice_aqc_nvm {
-       u8      cmd_flags;
+       __le16 offset_low;
+       u8 offset_high;
+       u8 cmd_flags;
 #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
@@ -1058,12 +1060,11 @@ struct ice_aqc_nvm {
 #define ICE_AQC_NVM_PRESERVE_ALL       BIT(1)
 #define ICE_AQC_NVM_PRESERVE_SELECTED  (3 << CSR_AQ_NVM_PRESERVATION_S)
 #define ICE_AQC_NVM_FLASH_ONLY         BIT(7)
-       u8      module_typeid;
-       __le16  length;
+       __le16 module_typeid;
+       __le16 length;
 #define ICE_AQC_NVM_ERASE_LEN  0xFFFF
-       __le32  offset;
-       __le32  addr_high;
-       __le32  addr_low;
+       __le32 addr_high;
+       __le32 addr_low;
 };
 
 /* Get/Set RSS key (indirect 0x0B04/0x0B02) */
index fa7a69a..92da0a6 100644 (file)
@@ -16,7 +16,7 @@
  * Read the NVM using the admin queue commands (0x0701)
  */
 static enum ice_status
-ice_aq_read_nvm(struct ice_hw *hw, u8 module_typeid, u32 offset, u16 length,
+ice_aq_read_nvm(struct ice_hw *hw, u16 module_typeid, u32 offset, u16 length,
                void *data, bool last_command, struct ice_sq_cd *cd)
 {
        struct ice_aq_desc desc;
@@ -33,8 +33,9 @@ ice_aq_read_nvm(struct ice_hw *hw, u8 module_typeid, u32 offset, u16 length,
        /* If this is the last command in a series, set the proper flag. */
        if (last_command)
                cmd->cmd_flags |= ICE_AQC_NVM_LAST_CMD;
-       cmd->module_typeid = module_typeid;
-       cmd->offset = cpu_to_le32(offset);
+       cmd->module_typeid = cpu_to_le16(module_typeid);
+       cmd->offset_low = cpu_to_le16(offset & 0xFFFF);
+       cmd->offset_high = (offset >> 16) & 0xFF;
        cmd->length = cpu_to_le16(length);
 
        return ice_aq_send_cmd(hw, &desc, data, length, cd);
index 42f63b9..1e49716 100644 (file)
@@ -1436,7 +1436,8 @@ void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
 {
 
        u32 hi_hash_dword, lo_hash_dword, flow_vm_vlan;
-       u32 bucket_hash = 0, hi_dword = 0;
+       u32 bucket_hash = 0;
+       __be32 hi_dword = 0;
        int i;
 
        /* Apply masks to input data */
@@ -1475,7 +1476,7 @@ void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
         * Limit hash to 13 bits since max bucket count is 8K.
         * Store result at the end of the input stream.
         */
-       input->formatted.bkt_hash = bucket_hash & 0x1FFF;
+       input->formatted.bkt_hash = (__force __be16)(bucket_hash & 0x1FFF);
 }
 
 /**
@@ -1584,7 +1585,7 @@ s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw,
                return IXGBE_ERR_CONFIG;
        }
 
-       switch (input_mask->formatted.flex_bytes & 0xFFFF) {
+       switch ((__force u16)input_mask->formatted.flex_bytes & 0xFFFF) {
        case 0x0000:
                /* Mask Flex Bytes */
                fdirm |= IXGBE_FDIRM_FLEX;
@@ -1654,13 +1655,13 @@ s32 ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw,
        IXGBE_WRITE_REG(hw, IXGBE_FDIRPORT, fdirport);
 
        /* record vlan (little-endian) and flex_bytes(big-endian) */
-       fdirvlan = IXGBE_STORE_AS_BE16(input->formatted.flex_bytes);
+       fdirvlan = IXGBE_STORE_AS_BE16((__force u16)input->formatted.flex_bytes);
        fdirvlan <<= IXGBE_FDIRVLAN_FLEX_SHIFT;
        fdirvlan |= ntohs(input->formatted.vlan_id);
        IXGBE_WRITE_REG(hw, IXGBE_FDIRVLAN, fdirvlan);
 
        /* configure FDIRHASH register */
-       fdirhash = input->formatted.bkt_hash;
+       fdirhash = (__force u32)input->formatted.bkt_hash;
        fdirhash |= soft_id << IXGBE_FDIRHASH_SIG_SW_INDEX_SHIFT;
        IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash);
 
@@ -1698,7 +1699,7 @@ s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw,
        s32 err;
 
        /* configure FDIRHASH register */
-       fdirhash = input->formatted.bkt_hash;
+       fdirhash = (__force u32)input->formatted.bkt_hash;
        fdirhash |= soft_id << IXGBE_FDIRHASH_SIG_SW_INDEX_SHIFT;
        IXGBE_WRITE_REG(hw, IXGBE_FDIRHASH, fdirhash);
 
index 8d03883..3f5c350 100644 (file)
@@ -3626,7 +3626,7 @@ s32 ixgbe_hic_unlocked(struct ixgbe_hw *hw, u32 *buffer, u32 length,
         */
        for (i = 0; i < dword_len; i++)
                IXGBE_WRITE_REG_ARRAY(hw, IXGBE_FLEX_MNG,
-                                     i, cpu_to_le32(buffer[i]));
+                                     i, (__force u32)cpu_to_le32(buffer[i]));
 
        /* Setting this bit tells the ARC that a new command is pending. */
        IXGBE_WRITE_REG(hw, IXGBE_HICR, hicr | IXGBE_HICR_C);
index b5219e0..94b3165 100644 (file)
@@ -440,7 +440,7 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
        case cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_FCPRSP):
                dma_unmap_sg(&adapter->pdev->dev, ddp->sgl,
                             ddp->sgc, DMA_FROM_DEVICE);
-               ddp->err = ddp_err;
+               ddp->err = (__force u32)ddp_err;
                ddp->sgl = NULL;
                ddp->sgc = 0;
                /* fall through */
index 195c0b6..99b170f 100644 (file)
@@ -19,8 +19,9 @@ static void ixgbe_ipsec_set_tx_sa(struct ixgbe_hw *hw, u16 idx,
        int i;
 
        for (i = 0; i < 4; i++)
-               IXGBE_WRITE_REG(hw, IXGBE_IPSTXKEY(i), cpu_to_be32(key[3 - i]));
-       IXGBE_WRITE_REG(hw, IXGBE_IPSTXSALT, cpu_to_be32(salt));
+               IXGBE_WRITE_REG(hw, IXGBE_IPSTXKEY(i),
+                               (__force u32)cpu_to_be32(key[3 - i]));
+       IXGBE_WRITE_REG(hw, IXGBE_IPSTXSALT, (__force u32)cpu_to_be32(salt));
        IXGBE_WRITE_FLUSH(hw);
 
        reg = IXGBE_READ_REG(hw, IXGBE_IPSTXIDX);
@@ -69,7 +70,8 @@ static void ixgbe_ipsec_set_rx_sa(struct ixgbe_hw *hw, u16 idx, __be32 spi,
        int i;
 
        /* store the SPI (in bigendian) and IPidx */
-       IXGBE_WRITE_REG(hw, IXGBE_IPSRXSPI, cpu_to_le32(spi));
+       IXGBE_WRITE_REG(hw, IXGBE_IPSRXSPI,
+                       (__force u32)cpu_to_le32((__force u32)spi));
        IXGBE_WRITE_REG(hw, IXGBE_IPSRXIPIDX, ip_idx);
        IXGBE_WRITE_FLUSH(hw);
 
@@ -77,8 +79,9 @@ static void ixgbe_ipsec_set_rx_sa(struct ixgbe_hw *hw, u16 idx, __be32 spi,
 
        /* store the key, salt, and mode */
        for (i = 0; i < 4; i++)
-               IXGBE_WRITE_REG(hw, IXGBE_IPSRXKEY(i), cpu_to_be32(key[3 - i]));
-       IXGBE_WRITE_REG(hw, IXGBE_IPSRXSALT, cpu_to_be32(salt));
+               IXGBE_WRITE_REG(hw, IXGBE_IPSRXKEY(i),
+                               (__force u32)cpu_to_be32(key[3 - i]));
+       IXGBE_WRITE_REG(hw, IXGBE_IPSRXSALT, (__force u32)cpu_to_be32(salt));
        IXGBE_WRITE_REG(hw, IXGBE_IPSRXMOD, mode);
        IXGBE_WRITE_FLUSH(hw);
 
@@ -97,7 +100,8 @@ static void ixgbe_ipsec_set_rx_ip(struct ixgbe_hw *hw, u16 idx, __be32 addr[])
 
        /* store the ip address */
        for (i = 0; i < 4; i++)
-               IXGBE_WRITE_REG(hw, IXGBE_IPSRXIPADDR(i), cpu_to_le32(addr[i]));
+               IXGBE_WRITE_REG(hw, IXGBE_IPSRXIPADDR(i),
+                               (__force u32)cpu_to_le32((__force u32)addr[i]));
        IXGBE_WRITE_FLUSH(hw);
 
        ixgbe_ipsec_set_rx_item(hw, idx, ips_rx_ip_tbl);
@@ -367,7 +371,8 @@ static struct xfrm_state *ixgbe_ipsec_find_rx_state(struct ixgbe_ipsec *ipsec,
        struct xfrm_state *ret = NULL;
 
        rcu_read_lock();
-       hash_for_each_possible_rcu(ipsec->rx_sa_list, rsa, hlist, spi)
+       hash_for_each_possible_rcu(ipsec->rx_sa_list, rsa, hlist,
+                                  (__force u32)spi) {
                if (spi == rsa->xs->id.spi &&
                    ((ip4 && *daddr == rsa->xs->id.daddr.a4) ||
                      (!ip4 && !memcmp(daddr, &rsa->xs->id.daddr.a6,
@@ -377,6 +382,7 @@ static struct xfrm_state *ixgbe_ipsec_find_rx_state(struct ixgbe_ipsec *ipsec,
                        xfrm_state_hold(ret);
                        break;
                }
+       }
        rcu_read_unlock();
        return ret;
 }
@@ -569,7 +575,7 @@ static int ixgbe_ipsec_add_sa(struct xfrm_state *xs)
 
                /* hash the new entry for faster search in Rx path */
                hash_add_rcu(ipsec->rx_sa_list, &ipsec->rx_tbl[sa_idx].hlist,
-                            rsa.xs->id.spi);
+                            (__force u64)rsa.xs->id.spi);
        } else {
                struct tx_sa tsa;
 
@@ -653,7 +659,8 @@ static void ixgbe_ipsec_del_sa(struct xfrm_state *xs)
                        if (!ipsec->ip_tbl[ipi].ref_cnt) {
                                memset(&ipsec->ip_tbl[ipi], 0,
                                       sizeof(struct rx_ip_sa));
-                               ixgbe_ipsec_set_rx_ip(hw, ipi, zerobuf);
+                               ixgbe_ipsec_set_rx_ip(hw, ipi,
+                                                     (__force __be32 *)zerobuf);
                        }
                }
 
index 6652b20..a52d92e 100644 (file)
@@ -727,8 +727,8 @@ static void ixgbe_dump(struct ixgbe_adapter *adapter)
                                        ring_desc = "";
                                pr_info("T [0x%03X]    %016llX %016llX %016llX %08X %p %016llX %p%s",
                                        i,
-                                       le64_to_cpu(u0->a),
-                                       le64_to_cpu(u0->b),
+                                       le64_to_cpu((__force __le64)u0->a),
+                                       le64_to_cpu((__force __le64)u0->b),
                                        (u64)dma_unmap_addr(tx_buffer, dma),
                                        dma_unmap_len(tx_buffer, len),
                                        tx_buffer->next_to_watch,
@@ -839,15 +839,15 @@ rx_ring_summary:
                                /* Descriptor Done */
                                pr_info("RWB[0x%03X]     %016llX %016llX ---------------- %p%s\n",
                                        i,
-                                       le64_to_cpu(u0->a),
-                                       le64_to_cpu(u0->b),
+                                       le64_to_cpu((__force __le64)u0->a),
+                                       le64_to_cpu((__force __le64)u0->b),
                                        rx_buffer_info->skb,
                                        ring_desc);
                        } else {
                                pr_info("R  [0x%03X]     %016llX %016llX %016llX %p%s\n",
                                        i,
-                                       le64_to_cpu(u0->a),
-                                       le64_to_cpu(u0->b),
+                                       le64_to_cpu((__force __le64)u0->a),
+                                       le64_to_cpu((__force __le64)u0->b),
                                        (u64)rx_buffer_info->dma,
                                        rx_buffer_info->skb,
                                        ring_desc);
@@ -7751,7 +7751,7 @@ static int ixgbe_tso(struct ixgbe_ring *tx_ring,
 
        /* remove payload length from inner checksum */
        paylen = skb->len - l4_offset;
-       csum_replace_by_diff(&l4.tcp->check, htonl(paylen));
+       csum_replace_by_diff(&l4.tcp->check, (__force __wsum)htonl(paylen));
 
        /* update gso size and bytecount with header size */
        first->gso_segs = skb_shinfo(skb)->gso_segs;
@@ -9104,7 +9104,8 @@ static int ixgbe_clsu32_build_input(struct ixgbe_fdir_filter *input,
 
                for (j = 0; field_ptr[j].val; j++) {
                        if (field_ptr[j].off == off) {
-                               field_ptr[j].val(input, mask, val, m);
+                               field_ptr[j].val(input, mask, (__force u32)val,
+                                                (__force u32)m);
                                input->filter.formatted.flow_type |=
                                        field_ptr[j].type;
                                found_entry = true;
@@ -9113,8 +9114,10 @@ static int ixgbe_clsu32_build_input(struct ixgbe_fdir_filter *input,
                }
                if (nexthdr) {
                        if (nexthdr->off == cls->knode.sel->keys[i].off &&
-                           nexthdr->val == cls->knode.sel->keys[i].val &&
-                           nexthdr->mask == cls->knode.sel->keys[i].mask)
+                           nexthdr->val ==
+                           (__force u32)cls->knode.sel->keys[i].val &&
+                           nexthdr->mask ==
+                           (__force u32)cls->knode.sel->keys[i].mask)
                                found_jump_field = true;
                        else
                                continue;
@@ -9218,7 +9221,8 @@ static int ixgbe_configure_clsu32(struct ixgbe_adapter *adapter,
                for (i = 0; nexthdr[i].jump; i++) {
                        if (nexthdr[i].o != cls->knode.sel->offoff ||
                            nexthdr[i].s != cls->knode.sel->offshift ||
-                           nexthdr[i].m != cls->knode.sel->offmask)
+                           nexthdr[i].m !=
+                           (__force u32)cls->knode.sel->offmask)
                                return err;
 
                        jump = kzalloc(sizeof(*jump), GFP_KERNEL);
@@ -9991,7 +9995,8 @@ static int ixgbe_xdp_setup(struct net_device *dev, struct bpf_prog *prog)
                }
        } else {
                for (i = 0; i < adapter->num_rx_queues; i++)
-                       xchg(&adapter->rx_ring[i]->xdp_prog, adapter->xdp_prog);
+                       (void)xchg(&adapter->rx_ring[i]->xdp_prog,
+                           adapter->xdp_prog);
        }
 
        if (old_prog)
@@ -10930,14 +10935,14 @@ skip_bad_vf_detection:
        rtnl_lock();
        netif_device_detach(netdev);
 
+       if (netif_running(netdev))
+               ixgbe_close_suspend(adapter);
+
        if (state == pci_channel_io_perm_failure) {
                rtnl_unlock();
                return PCI_ERS_RESULT_DISCONNECT;
        }
 
-       if (netif_running(netdev))
-               ixgbe_close_suspend(adapter);
-
        if (!test_and_set_bit(__IXGBE_DISABLED, &adapter->state))
                pci_disable_device(pdev);
        rtnl_unlock();
index fcae520..1e6cf22 100644 (file)
@@ -29,8 +29,8 @@ static inline int ixgbe_mat_prgm_sip(struct ixgbe_fdir_filter *input,
                                     union ixgbe_atr_input *mask,
                                     u32 val, u32 m)
 {
-       input->filter.formatted.src_ip[0] = val;
-       mask->formatted.src_ip[0] = m;
+       input->filter.formatted.src_ip[0] = (__force __be32)val;
+       mask->formatted.src_ip[0] = (__force __be32)m;
        return 0;
 }
 
@@ -38,8 +38,8 @@ static inline int ixgbe_mat_prgm_dip(struct ixgbe_fdir_filter *input,
                                     union ixgbe_atr_input *mask,
                                     u32 val, u32 m)
 {
-       input->filter.formatted.dst_ip[0] = val;
-       mask->formatted.dst_ip[0] = m;
+       input->filter.formatted.dst_ip[0] = (__force __be32)val;
+       mask->formatted.dst_ip[0] = (__force __be32)m;
        return 0;
 }
 
@@ -55,10 +55,10 @@ static inline int ixgbe_mat_prgm_ports(struct ixgbe_fdir_filter *input,
                                       union ixgbe_atr_input *mask,
                                       u32 val, u32 m)
 {
-       input->filter.formatted.src_port = val & 0xffff;
-       mask->formatted.src_port = m & 0xffff;
-       input->filter.formatted.dst_port = val >> 16;
-       mask->formatted.dst_port = m >> 16;
+       input->filter.formatted.src_port = (__force __be16)(val & 0xffff);
+       mask->formatted.src_port = (__force __be16)(m & 0xffff);
+       input->filter.formatted.dst_port = (__force __be16)(val >> 16);
+       mask->formatted.dst_port = (__force __be16)(m >> 16);
 
        return 0;
 };
index 2649c06..6f59933 100644 (file)
@@ -854,14 +854,11 @@ static int ixgbe_vf_reset_msg(struct ixgbe_adapter *adapter, u32 vf)
 
        /* reply to reset with ack and vf mac address */
        msgbuf[0] = IXGBE_VF_RESET;
-       if (!is_zero_ether_addr(vf_mac)) {
+       if (!is_zero_ether_addr(vf_mac) && adapter->vfinfo[vf].pf_set_mac) {
                msgbuf[0] |= IXGBE_VT_MSGTYPE_ACK;
                memcpy(addr, vf_mac, ETH_ALEN);
        } else {
                msgbuf[0] |= IXGBE_VT_MSGTYPE_NACK;
-               dev_warn(&adapter->pdev->dev,
-                        "VF %d has no MAC address assigned, you may have to assign one manually\n",
-                        vf);
        }
 
        /*
index ac71ed7..a8148c7 100644 (file)
@@ -878,8 +878,9 @@ static s32 ixgbe_read_ee_hostif_buffer_X550(struct ixgbe_hw *hw,
                buffer.hdr.req.checksum = FW_DEFAULT_CHECKSUM;
 
                /* convert offset from words to bytes */
-               buffer.address = cpu_to_be32((offset + current_word) * 2);
-               buffer.length = cpu_to_be16(words_to_read * 2);
+               buffer.address = (__force u32)cpu_to_be32((offset +
+                                                          current_word) * 2);
+               buffer.length = (__force u16)cpu_to_be16(words_to_read * 2);
                buffer.pad2 = 0;
                buffer.pad3 = 0;
 
@@ -1089,9 +1090,9 @@ static s32 ixgbe_read_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 *data)
        buffer.hdr.req.checksum = FW_DEFAULT_CHECKSUM;
 
        /* convert offset from words to bytes */
-       buffer.address = cpu_to_be32(offset * 2);
+       buffer.address = (__force u32)cpu_to_be32(offset * 2);
        /* one word */
-       buffer.length = cpu_to_be16(sizeof(u16));
+       buffer.length = (__force u16)cpu_to_be16(sizeof(u16));
 
        status = hw->mac.ops.acquire_swfw_sync(hw, mask);
        if (status)
index 1ccce6c..0830411 100644 (file)
@@ -4164,6 +4164,7 @@ static int ixgbevf_set_mac(struct net_device *netdev, void *p)
                return -EPERM;
 
        ether_addr_copy(hw->mac.addr, addr->sa_data);
+       ether_addr_copy(hw->mac.perm_addr, addr->sa_data);
        ether_addr_copy(netdev->dev_addr, addr->sa_data);
 
        return 0;
@@ -4747,14 +4748,14 @@ static pci_ers_result_t ixgbevf_io_error_detected(struct pci_dev *pdev,
        rtnl_lock();
        netif_device_detach(netdev);
 
+       if (netif_running(netdev))
+               ixgbevf_close_suspend(adapter);
+
        if (state == pci_channel_io_perm_failure) {
                rtnl_unlock();
                return PCI_ERS_RESULT_DISCONNECT;
        }
 
-       if (netif_running(netdev))
-               ixgbevf_close_suspend(adapter);
-
        if (!test_and_set_bit(__IXGBEVF_DISABLED, &adapter->state))
                pci_disable_device(pdev);
        rtnl_unlock();
index ebe5c91..cc2f770 100644 (file)
@@ -86,6 +86,7 @@ config MVPP2
        depends on ARCH_MVEBU || COMPILE_TEST
        depends on HAS_DMA
        select MVMDIO
+       select PHYLINK
        ---help---
          This driver supports the network interface units in the
          Marvell ARMADA 375, 7K and 8K SoCs.
index 0495487..c5dac6b 100644 (file)
@@ -348,10 +348,7 @@ static int orion_mdio_probe(struct platform_device *pdev)
                goto out_mdio;
        }
 
-       if (pdev->dev.of_node)
-               ret = of_mdiobus_register(bus, pdev->dev.of_node);
-       else
-               ret = mdiobus_register(bus);
+       ret = of_mdiobus_register(bus, pdev->dev.of_node);
        if (ret < 0) {
                dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
                goto out_mdio;
index 6f41023..6847cd4 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/phy.h>
+#include <linux/phylink.h>
 #include <linux/phy/phy.h>
 #include <linux/clk.h>
 #include <linux/hrtimer.h>
 #define     MVPP2_GMAC_FORCE_LINK_PASS         BIT(1)
 #define     MVPP2_GMAC_IN_BAND_AUTONEG         BIT(2)
 #define     MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS  BIT(3)
+#define     MVPP2_GMAC_IN_BAND_RESTART_AN      BIT(4)
 #define     MVPP2_GMAC_CONFIG_MII_SPEED        BIT(5)
 #define     MVPP2_GMAC_CONFIG_GMII_SPEED       BIT(6)
 #define     MVPP2_GMAC_AN_SPEED_EN             BIT(7)
 #define     MVPP2_GMAC_FC_ADV_EN               BIT(9)
+#define     MVPP2_GMAC_FC_ADV_ASM_EN           BIT(10)
 #define     MVPP2_GMAC_FLOW_CTRL_AUTONEG       BIT(11)
 #define     MVPP2_GMAC_CONFIG_FULL_DUPLEX      BIT(12)
 #define     MVPP2_GMAC_AN_DUPLEX_EN            BIT(13)
 #define MVPP2_GMAC_STATUS0                     0x10
 #define     MVPP2_GMAC_STATUS0_LINK_UP         BIT(0)
+#define     MVPP2_GMAC_STATUS0_GMII_SPEED      BIT(1)
+#define     MVPP2_GMAC_STATUS0_MII_SPEED       BIT(2)
+#define     MVPP2_GMAC_STATUS0_FULL_DUPLEX     BIT(3)
+#define     MVPP2_GMAC_STATUS0_RX_PAUSE                BIT(6)
+#define     MVPP2_GMAC_STATUS0_TX_PAUSE                BIT(7)
+#define     MVPP2_GMAC_STATUS0_AN_COMPLETE     BIT(11)
 #define MVPP2_GMAC_PORT_FIFO_CFG_1_REG         0x1c
 #define     MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS     6
 #define     MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0
 #define     MVPP22_GMAC_INT_MASK_LINK_STAT     BIT(1)
 #define MVPP22_GMAC_CTRL_4_REG                 0x90
 #define     MVPP22_CTRL4_EXT_PIN_GMII_SEL      BIT(0)
+#define     MVPP22_CTRL4_RX_FC_EN              BIT(3)
+#define     MVPP22_CTRL4_TX_FC_EN              BIT(4)
 #define     MVPP22_CTRL4_DP_CLK_SEL            BIT(5)
 #define     MVPP22_CTRL4_SYNC_BYPASS_DIS       BIT(6)
 #define     MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE  BIT(7)
 #define     MVPP22_XLG_CTRL0_PORT_EN           BIT(0)
 #define     MVPP22_XLG_CTRL0_MAC_RESET_DIS     BIT(1)
 #define     MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN   BIT(7)
+#define     MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN   BIT(8)
 #define     MVPP22_XLG_CTRL0_MIB_CNT_DIS       BIT(14)
 #define MVPP22_XLG_CTRL1_REG                   0x104
 #define     MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS       0
 #define     MVPP22_XLG_CTRL4_FWD_FC            BIT(5)
 #define     MVPP22_XLG_CTRL4_FWD_PFC           BIT(6)
 #define     MVPP22_XLG_CTRL4_MACMODSELECT_GMAC BIT(12)
+#define     MVPP22_XLG_CTRL4_EN_IDLE_CHECK     BIT(14)
 
 /* SMI registers. PPv2.2 only, relative to priv->iface_base. */
 #define MVPP22_SMI_MISC_CFG_REG                        0x1204
@@ -1017,6 +1030,9 @@ struct mvpp2_port {
        /* Firmware node associated to the port */
        struct fwnode_handle *fwnode;
 
+       /* Is a PHY always connected to the port */
+       bool has_phy;
+
        /* Per-port registers' base address */
        void __iomem *base;
        void __iomem *stats_base;
@@ -1044,12 +1060,11 @@ struct mvpp2_port {
        struct mutex gather_stats_lock;
        struct delayed_work stats_work;
 
+       struct device_node *of_node;
+
        phy_interface_t phy_interface;
-       struct device_node *phy_node;
+       struct phylink *phylink;
        struct phy *comphy;
-       unsigned int link;
-       unsigned int duplex;
-       unsigned int speed;
 
        struct mvpp2_bm_pool *pool_long;
        struct mvpp2_bm_pool *pool_short;
@@ -1338,6 +1353,12 @@ struct mvpp2_bm_pool {
         (addr) < (txq_pcpu)->tso_headers_dma + \
         (txq_pcpu)->size * TSO_HEADER_SIZE)
 
+/* The prototype is added here to be used in start_dev when using ACPI. This
+ * will be removed once phylink is used for all modes (dt+ACPI).
+ */
+static void mvpp2_mac_config(struct net_device *dev, unsigned int mode,
+                            const struct phylink_link_state *state);
+
 /* Queue modes */
 #define MVPP2_QDIST_SINGLE_MODE        0
 #define MVPP2_QDIST_MULTI_MODE 1
@@ -1735,7 +1756,6 @@ static void mvpp2_prs_tcam_ai_update(struct mvpp2_prs_entry *pe,
        int i, ai_idx = MVPP2_PRS_TCAM_AI_BYTE;
 
        for (i = 0; i < MVPP2_PRS_AI_BITS; i++) {
-
                if (!(enable & BIT(i)))
                        continue;
 
@@ -1819,7 +1839,6 @@ static void mvpp2_prs_sram_ai_update(struct mvpp2_prs_entry *pe,
        int ai_off = MVPP2_PRS_SRAM_AI_OFFS;
 
        for (i = 0; i < MVPP2_PRS_SRAM_AI_CTRL_BITS; i++) {
-
                if (!(mask & BIT(i)))
                        continue;
 
@@ -2109,6 +2128,9 @@ static void mvpp2_prs_dsa_tag_set(struct mvpp2 *priv, int port, bool add,
                                mvpp2_prs_sram_ai_update(&pe, 0,
                                                        MVPP2_PRS_SRAM_AI_MASK);
 
+                       /* Set result info bits to 'single vlan' */
+                       mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_SINGLE,
+                                                MVPP2_PRS_RI_VLAN_MASK);
                        /* If packet is tagged continue check vid filtering */
                        mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VID);
                } else {
@@ -4849,6 +4871,8 @@ static int mvpp22_gop_init(struct mvpp2_port *port)
                mvpp22_gop_init_rgmii(port);
                break;
        case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_2500BASEX:
                mvpp22_gop_init_sgmii(port);
                break;
        case PHY_INTERFACE_MODE_10GKR:
@@ -4886,7 +4910,9 @@ static void mvpp22_gop_unmask_irq(struct mvpp2_port *port)
        u32 val;
 
        if (phy_interface_mode_is_rgmii(port->phy_interface) ||
-           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+           port->phy_interface == PHY_INTERFACE_MODE_SGMII ||
+           port->phy_interface == PHY_INTERFACE_MODE_1000BASEX ||
+           port->phy_interface == PHY_INTERFACE_MODE_2500BASEX) {
                /* Enable the GMAC link status irq for this port */
                val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK);
                val |= MVPP22_GMAC_INT_SUM_MASK_LINK_STAT;
@@ -4911,12 +4937,14 @@ static void mvpp22_gop_mask_irq(struct mvpp2_port *port)
        if (port->gop_id == 0) {
                val = readl(port->base + MVPP22_XLG_EXT_INT_MASK);
                val &= ~(MVPP22_XLG_EXT_INT_MASK_XLG |
-                        MVPP22_XLG_EXT_INT_MASK_GIG);
+                        MVPP22_XLG_EXT_INT_MASK_GIG);
                writel(val, port->base + MVPP22_XLG_EXT_INT_MASK);
        }
 
        if (phy_interface_mode_is_rgmii(port->phy_interface) ||
-           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+           port->phy_interface == PHY_INTERFACE_MODE_SGMII ||
+           port->phy_interface == PHY_INTERFACE_MODE_1000BASEX ||
+           port->phy_interface == PHY_INTERFACE_MODE_2500BASEX) {
                val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK);
                val &= ~MVPP22_GMAC_INT_SUM_MASK_LINK_STAT;
                writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK);
@@ -4928,7 +4956,9 @@ static void mvpp22_gop_setup_irq(struct mvpp2_port *port)
        u32 val;
 
        if (phy_interface_mode_is_rgmii(port->phy_interface) ||
-           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+           port->phy_interface == PHY_INTERFACE_MODE_SGMII ||
+           port->phy_interface == PHY_INTERFACE_MODE_1000BASEX ||
+           port->phy_interface == PHY_INTERFACE_MODE_2500BASEX) {
                val = readl(port->base + MVPP22_GMAC_INT_MASK);
                val |= MVPP22_GMAC_INT_MASK_LINK_STAT;
                writel(val, port->base + MVPP22_GMAC_INT_MASK);
@@ -4943,6 +4973,16 @@ static void mvpp22_gop_setup_irq(struct mvpp2_port *port)
        mvpp22_gop_unmask_irq(port);
 }
 
+/* Sets the PHY mode of the COMPHY (which configures the serdes lanes).
+ *
+ * The PHY mode used by the PPv2 driver comes from the network subsystem, while
+ * the one given to the COMPHY comes from the generic PHY subsystem. Hence they
+ * differ.
+ *
+ * The COMPHY configures the serdes lanes regardless of the actual use of the
+ * lanes by the physical layer. This is why configurations like
+ * "PPv2 (2500BaseX) - COMPHY (2500SGMII)" are valid.
+ */
 static int mvpp22_comphy_init(struct mvpp2_port *port)
 {
        enum phy_mode mode;
@@ -4953,8 +4993,12 @@ static int mvpp22_comphy_init(struct mvpp2_port *port)
 
        switch (port->phy_interface) {
        case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
                mode = PHY_MODE_SGMII;
                break;
+       case PHY_INTERFACE_MODE_2500BASEX:
+               mode = PHY_MODE_2500SGMII;
+               break;
        case PHY_INTERFACE_MODE_10GKR:
                mode = PHY_MODE_10GKR;
                break;
@@ -4969,133 +5013,6 @@ static int mvpp22_comphy_init(struct mvpp2_port *port)
        return phy_power_on(port->comphy);
 }
 
-static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port)
-{
-       u32 val;
-
-       if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
-               val = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
-               val |= MVPP22_CTRL4_SYNC_BYPASS_DIS | MVPP22_CTRL4_DP_CLK_SEL |
-                      MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
-               val &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL;
-               writel(val, port->base + MVPP22_GMAC_CTRL_4_REG);
-       } else if (phy_interface_mode_is_rgmii(port->phy_interface)) {
-               val = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
-               val |= MVPP22_CTRL4_EXT_PIN_GMII_SEL |
-                      MVPP22_CTRL4_SYNC_BYPASS_DIS |
-                      MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
-               val &= ~MVPP22_CTRL4_DP_CLK_SEL;
-               writel(val, port->base + MVPP22_GMAC_CTRL_4_REG);
-       }
-
-       /* The port is connected to a copper PHY */
-       val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
-       val &= ~MVPP2_GMAC_PORT_TYPE_MASK;
-       writel(val, port->base + MVPP2_GMAC_CTRL_0_REG);
-
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       val |= MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS |
-              MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG |
-              MVPP2_GMAC_AN_DUPLEX_EN;
-       if (port->phy_interface == PHY_INTERFACE_MODE_SGMII)
-               val |= MVPP2_GMAC_IN_BAND_AUTONEG;
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-}
-
-static void mvpp2_port_mii_gmac_configure(struct mvpp2_port *port)
-{
-       u32 val;
-
-       /* Force link down */
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
-       val |= MVPP2_GMAC_FORCE_LINK_DOWN;
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-
-       /* Set the GMAC in a reset state */
-       val = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
-       val |= MVPP2_GMAC_PORT_RESET_MASK;
-       writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
-
-       /* Configure the PCS and in-band AN */
-       val = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
-       if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
-               val |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK;
-       } else if (phy_interface_mode_is_rgmii(port->phy_interface)) {
-               val &= ~MVPP2_GMAC_PCS_ENABLE_MASK;
-       }
-       writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
-
-       mvpp2_port_mii_gmac_configure_mode(port);
-
-       /* Unset the GMAC reset state */
-       val = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
-       val &= ~MVPP2_GMAC_PORT_RESET_MASK;
-       writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
-
-       /* Stop forcing link down */
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       val &= ~MVPP2_GMAC_FORCE_LINK_DOWN;
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-}
-
-static void mvpp2_port_mii_xlg_configure(struct mvpp2_port *port)
-{
-       u32 val;
-
-       if (port->gop_id != 0)
-               return;
-
-       val = readl(port->base + MVPP22_XLG_CTRL0_REG);
-       val |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN;
-       writel(val, port->base + MVPP22_XLG_CTRL0_REG);
-
-       val = readl(port->base + MVPP22_XLG_CTRL4_REG);
-       val &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC;
-       val |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC;
-       writel(val, port->base + MVPP22_XLG_CTRL4_REG);
-}
-
-static void mvpp22_port_mii_set(struct mvpp2_port *port)
-{
-       u32 val;
-
-       /* Only GOP port 0 has an XLG MAC */
-       if (port->gop_id == 0) {
-               val = readl(port->base + MVPP22_XLG_CTRL3_REG);
-               val &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
-
-               if (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
-                   port->phy_interface == PHY_INTERFACE_MODE_10GKR)
-                       val |= MVPP22_XLG_CTRL3_MACMODESELECT_10G;
-               else
-                       val |= MVPP22_XLG_CTRL3_MACMODESELECT_GMAC;
-
-               writel(val, port->base + MVPP22_XLG_CTRL3_REG);
-       }
-}
-
-static void mvpp2_port_mii_set(struct mvpp2_port *port)
-{
-       if (port->priv->hw_version == MVPP22)
-               mvpp22_port_mii_set(port);
-
-       if (phy_interface_mode_is_rgmii(port->phy_interface) ||
-           port->phy_interface == PHY_INTERFACE_MODE_SGMII)
-               mvpp2_port_mii_gmac_configure(port);
-       else if (port->phy_interface == PHY_INTERFACE_MODE_10GKR)
-               mvpp2_port_mii_xlg_configure(port);
-}
-
-static void mvpp2_port_fc_adv_enable(struct mvpp2_port *port)
-{
-       u32 val;
-
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       val |= MVPP2_GMAC_FC_ADV_EN;
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-}
-
 static void mvpp2_port_enable(struct mvpp2_port *port)
 {
        u32 val;
@@ -5126,8 +5043,11 @@ static void mvpp2_port_disable(struct mvpp2_port *port)
            (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
             port->phy_interface == PHY_INTERFACE_MODE_10GKR)) {
                val = readl(port->base + MVPP22_XLG_CTRL0_REG);
-               val &= ~(MVPP22_XLG_CTRL0_PORT_EN |
-                        MVPP22_XLG_CTRL0_MAC_RESET_DIS);
+               val &= ~MVPP22_XLG_CTRL0_PORT_EN;
+               writel(val, port->base + MVPP22_XLG_CTRL0_REG);
+
+               /* Disable & reset should be done separately */
+               val &= ~MVPP22_XLG_CTRL0_MAC_RESET_DIS;
                writel(val, port->base + MVPP22_XLG_CTRL0_REG);
        } else {
                val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
@@ -5147,18 +5067,21 @@ static void mvpp2_port_periodic_xon_disable(struct mvpp2_port *port)
 }
 
 /* Configure loopback port */
-static void mvpp2_port_loopback_set(struct mvpp2_port *port)
+static void mvpp2_port_loopback_set(struct mvpp2_port *port,
+                                   const struct phylink_link_state *state)
 {
        u32 val;
 
        val = readl(port->base + MVPP2_GMAC_CTRL_1_REG);
 
-       if (port->speed == 1000)
+       if (state->speed == 1000)
                val |= MVPP2_GMAC_GMII_LB_EN_MASK;
        else
                val &= ~MVPP2_GMAC_GMII_LB_EN_MASK;
 
-       if (port->phy_interface == PHY_INTERFACE_MODE_SGMII)
+       if (port->phy_interface == PHY_INTERFACE_MODE_SGMII ||
+           port->phy_interface == PHY_INTERFACE_MODE_1000BASEX ||
+           port->phy_interface == PHY_INTERFACE_MODE_2500BASEX)
                val |= MVPP2_GMAC_PCS_LB_EN_MASK;
        else
                val &= ~MVPP2_GMAC_PCS_LB_EN_MASK;
@@ -5331,10 +5254,6 @@ static void mvpp2_defaults_set(struct mvpp2_port *port)
        int tx_port_num, val, queue, ptxq, lrxq;
 
        if (port->priv->hw_version == MVPP21) {
-               /* Configure port to loopback if needed */
-               if (port->flags & MVPP2_F_LOOPBACK)
-                       mvpp2_port_loopback_set(port);
-
                /* Update TX FIFO MIN Threshold */
                val = readl(port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG);
                val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK;
@@ -5552,7 +5471,6 @@ static void mvpp2_aggr_txq_pend_desc_add(struct mvpp2_port *port, int pending)
                           MVPP2_AGGR_TXQ_UPDATE_REG, pending);
 }
 
-
 /* Check if there are enough free descriptors in aggregated txq.
  * If not, update the number of occupied descriptors and repeat the check.
  *
@@ -5569,11 +5487,10 @@ static int mvpp2_aggr_desc_num_check(struct mvpp2 *priv,
                                             MVPP2_AGGR_TXQ_STATUS_REG(cpu));
 
                aggr_txq->count = val & MVPP2_AGGR_TXQ_PENDING_MASK;
-       }
-
-       if ((aggr_txq->count + num) > MVPP2_AGGR_TXQ_SIZE)
-               return -ENOMEM;
 
+               if ((aggr_txq->count + num) > MVPP2_AGGR_TXQ_SIZE)
+                       return -ENOMEM;
+       }
        return 0;
 }
 
@@ -5633,7 +5550,7 @@ static int mvpp2_txq_reserved_desc_num_proc(struct mvpp2 *priv,
 
        txq_pcpu->reserved_num += mvpp2_txq_alloc_reserved_desc(priv, txq, req);
 
-       /* OK, the descriptor cound has been updated: check again. */
+       /* OK, the descriptor could have been updated: check again. */
        if (txq_pcpu->reserved_num < num)
                return -ENOMEM;
        return 0;
@@ -6115,7 +6032,7 @@ static int mvpp2_txq_init(struct mvpp2_port *port,
        /* Calculate base address in prefetch buffer. We reserve 16 descriptors
         * for each existing TXQ.
         * TCONTS for PON port must be continuous from 0 to MVPP2_MAX_TCONT
-        * GBE ports assumed to be continious from 0 to MVPP2_MAX_PORTS
+        * GBE ports assumed to be continuous from 0 to MVPP2_MAX_PORTS
         */
        desc_per_txq = 16;
        desc = (port->id * MVPP2_MAX_TXQ * desc_per_txq) +
@@ -6372,7 +6289,9 @@ static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id)
                                link = true;
                }
        } else if (phy_interface_mode_is_rgmii(port->phy_interface) ||
-                  port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+                  port->phy_interface == PHY_INTERFACE_MODE_SGMII ||
+                  port->phy_interface == PHY_INTERFACE_MODE_1000BASEX ||
+                  port->phy_interface == PHY_INTERFACE_MODE_2500BASEX) {
                val = readl(port->base + MVPP22_GMAC_INT_STAT);
                if (val & MVPP22_GMAC_INT_STAT_LINK) {
                        event = true;
@@ -6382,6 +6301,11 @@ static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id)
                }
        }
 
+       if (port->phylink) {
+               phylink_mac_change(port->phylink, link);
+               goto handled;
+       }
+
        if (!netif_running(dev) || !event)
                goto handled;
 
@@ -6406,111 +6330,6 @@ handled:
        return IRQ_HANDLED;
 }
 
-static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port,
-                                  struct phy_device *phydev)
-{
-       u32 val;
-
-       if (port->phy_interface != PHY_INTERFACE_MODE_RGMII &&
-           port->phy_interface != PHY_INTERFACE_MODE_RGMII_ID &&
-           port->phy_interface != PHY_INTERFACE_MODE_RGMII_RXID &&
-           port->phy_interface != PHY_INTERFACE_MODE_RGMII_TXID &&
-           port->phy_interface != PHY_INTERFACE_MODE_SGMII)
-               return;
-
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED |
-                MVPP2_GMAC_CONFIG_GMII_SPEED |
-                MVPP2_GMAC_CONFIG_FULL_DUPLEX |
-                MVPP2_GMAC_AN_SPEED_EN |
-                MVPP2_GMAC_AN_DUPLEX_EN);
-
-       if (phydev->duplex)
-               val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
-
-       if (phydev->speed == SPEED_1000)
-               val |= MVPP2_GMAC_CONFIG_GMII_SPEED;
-       else if (phydev->speed == SPEED_100)
-               val |= MVPP2_GMAC_CONFIG_MII_SPEED;
-
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-}
-
-/* Adjust link */
-static void mvpp2_link_event(struct net_device *dev)
-{
-       struct mvpp2_port *port = netdev_priv(dev);
-       struct phy_device *phydev = dev->phydev;
-       bool link_reconfigured = false;
-       u32 val;
-
-       if (phydev->link) {
-               if (port->phy_interface != phydev->interface && port->comphy) {
-                       /* disable current port for reconfiguration */
-                       mvpp2_interrupts_disable(port);
-                       netif_carrier_off(port->dev);
-                       mvpp2_port_disable(port);
-                       phy_power_off(port->comphy);
-
-                       /* comphy reconfiguration */
-                       port->phy_interface = phydev->interface;
-                       mvpp22_comphy_init(port);
-
-                       /* gop/mac reconfiguration */
-                       mvpp22_gop_init(port);
-                       mvpp2_port_mii_set(port);
-
-                       link_reconfigured = true;
-               }
-
-               if ((port->speed != phydev->speed) ||
-                   (port->duplex != phydev->duplex)) {
-                       mvpp2_gmac_set_autoneg(port, phydev);
-
-                       port->duplex = phydev->duplex;
-                       port->speed  = phydev->speed;
-               }
-       }
-
-       if (phydev->link != port->link || link_reconfigured) {
-               port->link = phydev->link;
-
-               if (phydev->link) {
-                       if (port->phy_interface == PHY_INTERFACE_MODE_RGMII ||
-                           port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
-                           port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
-                           port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID ||
-                           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
-                               val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-                               val |= (MVPP2_GMAC_FORCE_LINK_PASS |
-                                       MVPP2_GMAC_FORCE_LINK_DOWN);
-                               writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-                       }
-
-                       mvpp2_interrupts_enable(port);
-                       mvpp2_port_enable(port);
-
-                       mvpp2_egress_enable(port);
-                       mvpp2_ingress_enable(port);
-                       netif_carrier_on(dev);
-                       netif_tx_wake_all_queues(dev);
-               } else {
-                       port->duplex = -1;
-                       port->speed = 0;
-
-                       netif_tx_stop_all_queues(dev);
-                       netif_carrier_off(dev);
-                       mvpp2_ingress_disable(port);
-                       mvpp2_egress_disable(port);
-
-                       mvpp2_port_disable(port);
-                       mvpp2_interrupts_disable(port);
-               }
-
-               phy_print_status(phydev);
-       }
-}
-
 static void mvpp2_timer_set(struct mvpp2_port_pcpu *port_pcpu)
 {
        ktime_t interval;
@@ -6562,21 +6381,23 @@ static void mvpp2_rx_error(struct mvpp2_port *port,
 {
        u32 status = mvpp2_rxdesc_status_get(port, rx_desc);
        size_t sz = mvpp2_rxdesc_size_get(port, rx_desc);
+       char *err_str = NULL;
 
        switch (status & MVPP2_RXD_ERR_CODE_MASK) {
        case MVPP2_RXD_ERR_CRC:
-               netdev_err(port->dev, "bad rx status %08x (crc error), size=%zu\n",
-                          status, sz);
+               err_str = "crc";
                break;
        case MVPP2_RXD_ERR_OVERRUN:
-               netdev_err(port->dev, "bad rx status %08x (overrun error), size=%zu\n",
-                          status, sz);
+               err_str = "overrun";
                break;
        case MVPP2_RXD_ERR_RESOURCE:
-               netdev_err(port->dev, "bad rx status %08x (resource error), size=%zu\n",
-                          status, sz);
+               err_str = "resource";
                break;
        }
+       if (err_str && net_ratelimit())
+               netdev_err(port->dev,
+                          "bad rx status %08x (%s error), size=%zu\n",
+                          status, err_str, sz);
 }
 
 /* Handle RX checksum offload */
@@ -6781,8 +6602,7 @@ static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb,
                mvpp2_txdesc_size_set(port, tx_desc, frag->size);
 
                buf_dma_addr = dma_map_single(port->dev->dev.parent, addr,
-                                              frag->size,
-                                              DMA_TO_DEVICE);
+                                             frag->size, DMA_TO_DEVICE);
                if (dma_mapping_error(port->dev->dev.parent, buf_dma_addr)) {
                        mvpp2_txq_desc_put(txq);
                        goto cleanup;
@@ -7118,11 +6938,29 @@ static int mvpp2_poll(struct napi_struct *napi, int budget)
        return rx_done;
 }
 
-/* Set hw internals when starting port */
-static void mvpp2_start_dev(struct mvpp2_port *port)
+static void mvpp22_mode_reconfigure(struct mvpp2_port *port)
 {
-       struct net_device *ndev = port->dev;
-       int i;
+       u32 ctrl3;
+
+       /* comphy reconfiguration */
+       mvpp22_comphy_init(port);
+
+       /* gop reconfiguration */
+       mvpp22_gop_init(port);
+
+       /* Only GOP port 0 has an XLG MAC */
+       if (port->gop_id == 0) {
+               ctrl3 = readl(port->base + MVPP22_XLG_CTRL3_REG);
+               ctrl3 &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
+
+               if (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
+                   port->phy_interface == PHY_INTERFACE_MODE_10GKR)
+                       ctrl3 |= MVPP22_XLG_CTRL3_MACMODESELECT_10G;
+               else
+                       ctrl3 |= MVPP22_XLG_CTRL3_MACMODESELECT_GMAC;
+
+               writel(ctrl3, port->base + MVPP22_XLG_CTRL3_REG);
+       }
 
        if (port->gop_id == 0 &&
            (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
@@ -7130,6 +6968,12 @@ static void mvpp2_start_dev(struct mvpp2_port *port)
                mvpp2_xlg_max_rx_size_set(port);
        else
                mvpp2_gmac_max_rx_size_set(port);
+}
+
+/* Set hw internals when starting port */
+static void mvpp2_start_dev(struct mvpp2_port *port)
+{
+       int i;
 
        mvpp2_txp_max_tx_size_set(port);
 
@@ -7139,42 +6983,39 @@ static void mvpp2_start_dev(struct mvpp2_port *port)
        /* Enable interrupts on all CPUs */
        mvpp2_interrupts_enable(port);
 
-       if (port->priv->hw_version == MVPP22) {
-               mvpp22_comphy_init(port);
-               mvpp22_gop_init(port);
+       if (port->priv->hw_version == MVPP22)
+               mvpp22_mode_reconfigure(port);
+
+       if (port->phylink) {
+               phylink_start(port->phylink);
+       } else {
+               /* Phylink isn't used as of now for ACPI, so the MAC has to be
+                * configured manually when the interface is started. This will
+                * be removed as soon as the phylink ACPI support lands in.
+                */
+               struct phylink_link_state state = {
+                       .interface = port->phy_interface,
+                       .link = 1,
+               };
+               mvpp2_mac_config(port->dev, MLO_AN_INBAND, &state);
        }
 
-       mvpp2_port_mii_set(port);
-       mvpp2_port_enable(port);
-       if (ndev->phydev)
-               phy_start(ndev->phydev);
        netif_tx_start_all_queues(port->dev);
 }
 
 /* Set hw internals when stopping port */
 static void mvpp2_stop_dev(struct mvpp2_port *port)
 {
-       struct net_device *ndev = port->dev;
        int i;
 
-       /* Stop new packets from arriving to RXQs */
-       mvpp2_ingress_disable(port);
-
-       mdelay(10);
-
        /* Disable interrupts on all CPUs */
        mvpp2_interrupts_disable(port);
 
        for (i = 0; i < port->nqvecs; i++)
                napi_disable(&port->qvecs[i].napi);
 
-       netif_carrier_off(port->dev);
-       netif_tx_stop_all_queues(port->dev);
-
-       mvpp2_egress_disable(port);
-       mvpp2_port_disable(port);
-       if (ndev->phydev)
-               phy_stop(ndev->phydev);
+       if (port->phylink)
+               phylink_stop(port->phylink);
        phy_power_off(port->comphy);
 }
 
@@ -7233,40 +7074,6 @@ static void mvpp21_get_mac_address(struct mvpp2_port *port, unsigned char *addr)
        addr[5] = (mac_addr_l >> MVPP2_GMAC_SA_LOW_OFFS) & 0xFF;
 }
 
-static int mvpp2_phy_connect(struct mvpp2_port *port)
-{
-       struct phy_device *phy_dev;
-
-       /* No PHY is attached */
-       if (!port->phy_node)
-               return 0;
-
-       phy_dev = of_phy_connect(port->dev, port->phy_node, mvpp2_link_event, 0,
-                                port->phy_interface);
-       if (!phy_dev) {
-               netdev_err(port->dev, "cannot connect to phy\n");
-               return -ENODEV;
-       }
-       phy_dev->supported &= PHY_GBIT_FEATURES;
-       phy_dev->advertising = phy_dev->supported;
-
-       port->link    = 0;
-       port->duplex  = 0;
-       port->speed   = 0;
-
-       return 0;
-}
-
-static void mvpp2_phy_disconnect(struct mvpp2_port *port)
-{
-       struct net_device *ndev = port->dev;
-
-       if (!ndev->phydev)
-               return;
-
-       phy_disconnect(ndev->phydev);
-}
-
 static int mvpp2_irqs_init(struct mvpp2_port *port)
 {
        int err, i;
@@ -7350,6 +7157,7 @@ static int mvpp2_open(struct net_device *dev)
        struct mvpp2 *priv = port->priv;
        unsigned char mac_bcast[ETH_ALEN] = {
                        0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+       bool valid = false;
        int err;
 
        err = mvpp2_prs_mac_da_accept(port, mac_bcast, true);
@@ -7392,7 +7200,19 @@ static int mvpp2_open(struct net_device *dev)
                goto err_cleanup_txqs;
        }
 
-       if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) {
+       /* Phylink isn't supported yet in ACPI mode */
+       if (port->of_node) {
+               err = phylink_of_phy_connect(port->phylink, port->of_node, 0);
+               if (err) {
+                       netdev_err(port->dev, "could not attach PHY (%d)\n",
+                                  err);
+                       goto err_free_irq;
+               }
+
+               valid = true;
+       }
+
+       if (priv->hw_version == MVPP22 && port->link_irq && !port->phylink) {
                err = request_irq(port->link_irq, mvpp2_link_status_isr, 0,
                                  dev->name, port);
                if (err) {
@@ -7402,14 +7222,20 @@ static int mvpp2_open(struct net_device *dev)
                }
 
                mvpp22_gop_setup_irq(port);
-       }
 
-       /* In default link is down */
-       netif_carrier_off(port->dev);
+               /* In default link is down */
+               netif_carrier_off(port->dev);
 
-       err = mvpp2_phy_connect(port);
-       if (err < 0)
-               goto err_free_link_irq;
+               valid = true;
+       } else {
+               port->link_irq = 0;
+       }
+
+       if (!valid) {
+               netdev_err(port->dev,
+                          "invalid configuration: no dt or link IRQ");
+               goto err_free_irq;
+       }
 
        /* Unmask interrupts on all CPUs */
        on_each_cpu(mvpp2_interrupts_unmask, port, 1);
@@ -7426,9 +7252,6 @@ static int mvpp2_open(struct net_device *dev)
 
        return 0;
 
-err_free_link_irq:
-       if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq)
-               free_irq(port->link_irq, port);
 err_free_irq:
        mvpp2_irqs_deinit(port);
 err_cleanup_txqs:
@@ -7442,17 +7265,17 @@ static int mvpp2_stop(struct net_device *dev)
 {
        struct mvpp2_port *port = netdev_priv(dev);
        struct mvpp2_port_pcpu *port_pcpu;
-       struct mvpp2 *priv = port->priv;
        int cpu;
 
        mvpp2_stop_dev(port);
-       mvpp2_phy_disconnect(port);
 
        /* Mask interrupts on all CPUs */
        on_each_cpu(mvpp2_interrupts_mask, port, 1);
        mvpp2_shared_interrupt_mask_unmask(port, true);
 
-       if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq)
+       if (port->phylink)
+               phylink_disconnect_phy(port->phylink);
+       if (port->link_irq)
                free_irq(port->link_irq, port);
 
        mvpp2_irqs_deinit(port);
@@ -7535,42 +7358,18 @@ static void mvpp2_set_rx_mode(struct net_device *dev)
 
 static int mvpp2_set_mac_address(struct net_device *dev, void *p)
 {
-       struct mvpp2_port *port = netdev_priv(dev);
        const struct sockaddr *addr = p;
        int err;
 
-       if (!is_valid_ether_addr(addr->sa_data)) {
-               err = -EADDRNOTAVAIL;
-               goto log_error;
-       }
-
-       if (!netif_running(dev)) {
-               err = mvpp2_prs_update_mac_da(dev, addr->sa_data);
-               if (!err)
-                       return 0;
-               /* Reconfigure parser to accept the original MAC address */
-               err = mvpp2_prs_update_mac_da(dev, dev->dev_addr);
-               if (err)
-                       goto log_error;
-       }
-
-       mvpp2_stop_dev(port);
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
 
        err = mvpp2_prs_update_mac_da(dev, addr->sa_data);
-       if (!err)
-               goto out_start;
-
-       /* Reconfigure parser accept the original MAC address */
-       err = mvpp2_prs_update_mac_da(dev, dev->dev_addr);
-       if (err)
-               goto log_error;
-out_start:
-       mvpp2_start_dev(port);
-       mvpp2_egress_enable(port);
-       mvpp2_ingress_enable(port);
-       return 0;
-log_error:
-       netdev_err(dev, "failed to change MAC address\n");
+       if (err) {
+               /* Reconfigure parser accept the original MAC address */
+               mvpp2_prs_update_mac_da(dev, dev->dev_addr);
+               netdev_err(dev, "failed to change MAC address\n");
+       }
        return err;
 }
 
@@ -7658,16 +7457,12 @@ mvpp2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 
 static int mvpp2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-       int ret;
+       struct mvpp2_port *port = netdev_priv(dev);
 
-       if (!dev->phydev)
+       if (!port->phylink)
                return -ENOTSUPP;
 
-       ret = phy_mii_ioctl(dev->phydev, ifr, cmd);
-       if (!ret)
-               mvpp2_link_event(dev);
-
-       return ret;
+       return phylink_mii_ioctl(port->phylink, ifr, cmd);
 }
 
 static int mvpp2_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
@@ -7714,6 +7509,16 @@ static int mvpp2_set_features(struct net_device *dev,
 
 /* Ethtool methods */
 
+static int mvpp2_ethtool_nway_reset(struct net_device *dev)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (!port->phylink)
+               return -ENOTSUPP;
+
+       return phylink_ethtool_nway_reset(port->phylink);
+}
+
 /* Set interrupt coalescing for ethtools */
 static int mvpp2_ethtool_set_coalesce(struct net_device *dev,
                                      struct ethtool_coalesce *c)
@@ -7842,6 +7647,50 @@ err_out:
        return err;
 }
 
+static void mvpp2_ethtool_get_pause_param(struct net_device *dev,
+                                         struct ethtool_pauseparam *pause)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (!port->phylink)
+               return;
+
+       phylink_ethtool_get_pauseparam(port->phylink, pause);
+}
+
+static int mvpp2_ethtool_set_pause_param(struct net_device *dev,
+                                        struct ethtool_pauseparam *pause)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (!port->phylink)
+               return -ENOTSUPP;
+
+       return phylink_ethtool_set_pauseparam(port->phylink, pause);
+}
+
+static int mvpp2_ethtool_get_link_ksettings(struct net_device *dev,
+                                           struct ethtool_link_ksettings *cmd)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (!port->phylink)
+               return -ENOTSUPP;
+
+       return phylink_ethtool_ksettings_get(port->phylink, cmd);
+}
+
+static int mvpp2_ethtool_set_link_ksettings(struct net_device *dev,
+                                           const struct ethtool_link_ksettings *cmd)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (!port->phylink)
+               return -ENOTSUPP;
+
+       return phylink_ethtool_ksettings_set(port->phylink, cmd);
+}
+
 /* Device ops */
 
 static const struct net_device_ops mvpp2_netdev_ops = {
@@ -7859,18 +7708,20 @@ static const struct net_device_ops mvpp2_netdev_ops = {
 };
 
 static const struct ethtool_ops mvpp2_eth_tool_ops = {
-       .nway_reset     = phy_ethtool_nway_reset,
-       .get_link       = ethtool_op_get_link,
-       .set_coalesce   = mvpp2_ethtool_set_coalesce,
-       .get_coalesce   = mvpp2_ethtool_get_coalesce,
-       .get_drvinfo    = mvpp2_ethtool_get_drvinfo,
-       .get_ringparam  = mvpp2_ethtool_get_ringparam,
-       .set_ringparam  = mvpp2_ethtool_set_ringparam,
-       .get_strings    = mvpp2_ethtool_get_strings,
-       .get_ethtool_stats = mvpp2_ethtool_get_stats,
-       .get_sset_count = mvpp2_ethtool_get_sset_count,
-       .get_link_ksettings = phy_ethtool_get_link_ksettings,
-       .set_link_ksettings = phy_ethtool_set_link_ksettings,
+       .nway_reset             = mvpp2_ethtool_nway_reset,
+       .get_link               = ethtool_op_get_link,
+       .set_coalesce           = mvpp2_ethtool_set_coalesce,
+       .get_coalesce           = mvpp2_ethtool_get_coalesce,
+       .get_drvinfo            = mvpp2_ethtool_get_drvinfo,
+       .get_ringparam          = mvpp2_ethtool_get_ringparam,
+       .set_ringparam          = mvpp2_ethtool_set_ringparam,
+       .get_strings            = mvpp2_ethtool_get_strings,
+       .get_ethtool_stats      = mvpp2_ethtool_get_stats,
+       .get_sset_count         = mvpp2_ethtool_get_sset_count,
+       .get_pauseparam         = mvpp2_ethtool_get_pause_param,
+       .set_pauseparam         = mvpp2_ethtool_set_pause_param,
+       .get_link_ksettings     = mvpp2_ethtool_get_link_ksettings,
+       .set_link_ksettings     = mvpp2_ethtool_set_link_ksettings,
 };
 
 /* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that
@@ -8172,18 +8023,361 @@ static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv,
        eth_hw_addr_random(dev);
 }
 
+static void mvpp2_phylink_validate(struct net_device *dev,
+                                  unsigned long *supported,
+                                  struct phylink_link_state *state)
+{
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+       phylink_set(mask, Autoneg);
+       phylink_set_port_modes(mask);
+       phylink_set(mask, Pause);
+       phylink_set(mask, Asym_Pause);
+
+       switch (state->interface) {
+       case PHY_INTERFACE_MODE_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);
+               /* Fall-through */
+       default:
+               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);
+       }
+
+       bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+       bitmap_and(state->advertising, state->advertising, mask,
+                  __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static void mvpp22_xlg_link_state(struct mvpp2_port *port,
+                                 struct phylink_link_state *state)
+{
+       u32 val;
+
+       state->speed = SPEED_10000;
+       state->duplex = 1;
+       state->an_complete = 1;
+
+       val = readl(port->base + MVPP22_XLG_STATUS);
+       state->link = !!(val & MVPP22_XLG_STATUS_LINK_UP);
+
+       state->pause = 0;
+       val = readl(port->base + MVPP22_XLG_CTRL0_REG);
+       if (val & MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN)
+               state->pause |= MLO_PAUSE_TX;
+       if (val & MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN)
+               state->pause |= MLO_PAUSE_RX;
+}
+
+static void mvpp2_gmac_link_state(struct mvpp2_port *port,
+                                 struct phylink_link_state *state)
+{
+       u32 val;
+
+       val = readl(port->base + MVPP2_GMAC_STATUS0);
+
+       state->an_complete = !!(val & MVPP2_GMAC_STATUS0_AN_COMPLETE);
+       state->link = !!(val & MVPP2_GMAC_STATUS0_LINK_UP);
+       state->duplex = !!(val & MVPP2_GMAC_STATUS0_FULL_DUPLEX);
+
+       switch (port->phy_interface) {
+       case PHY_INTERFACE_MODE_1000BASEX:
+               state->speed = SPEED_1000;
+               break;
+       case PHY_INTERFACE_MODE_2500BASEX:
+               state->speed = SPEED_2500;
+               break;
+       default:
+               if (val & MVPP2_GMAC_STATUS0_GMII_SPEED)
+                       state->speed = SPEED_1000;
+               else if (val & MVPP2_GMAC_STATUS0_MII_SPEED)
+                       state->speed = SPEED_100;
+               else
+                       state->speed = SPEED_10;
+       }
+
+       state->pause = 0;
+       if (val & MVPP2_GMAC_STATUS0_RX_PAUSE)
+               state->pause |= MLO_PAUSE_RX;
+       if (val & MVPP2_GMAC_STATUS0_TX_PAUSE)
+               state->pause |= MLO_PAUSE_TX;
+}
+
+static int mvpp2_phylink_mac_link_state(struct net_device *dev,
+                                       struct phylink_link_state *state)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (port->priv->hw_version == MVPP22 && port->gop_id == 0) {
+               u32 mode = readl(port->base + MVPP22_XLG_CTRL3_REG);
+               mode &= MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
+
+               if (mode == MVPP22_XLG_CTRL3_MACMODESELECT_10G) {
+                       mvpp22_xlg_link_state(port, state);
+                       return 1;
+               }
+       }
+
+       mvpp2_gmac_link_state(port, state);
+       return 1;
+}
+
+static void mvpp2_mac_an_restart(struct net_device *dev)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+       u32 val;
+
+       if (port->phy_interface != PHY_INTERFACE_MODE_SGMII)
+               return;
+
+       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       /* The RESTART_AN bit is cleared by the h/w after restarting the AN
+        * process.
+        */
+       val |= MVPP2_GMAC_IN_BAND_RESTART_AN | MVPP2_GMAC_IN_BAND_AUTONEG;
+       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+}
+
+static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode,
+                            const struct phylink_link_state *state)
+{
+       u32 ctrl0, ctrl4;
+
+       ctrl0 = readl(port->base + MVPP22_XLG_CTRL0_REG);
+       ctrl4 = readl(port->base + MVPP22_XLG_CTRL4_REG);
+
+       if (state->pause & MLO_PAUSE_TX)
+               ctrl0 |= MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN;
+       if (state->pause & MLO_PAUSE_RX)
+               ctrl0 |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN;
+
+       ctrl4 &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC;
+       ctrl4 |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC |
+                MVPP22_XLG_CTRL4_EN_IDLE_CHECK;
+
+       writel(ctrl0, port->base + MVPP22_XLG_CTRL0_REG);
+       writel(ctrl4, port->base + MVPP22_XLG_CTRL4_REG);
+}
+
+static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode,
+                             const struct phylink_link_state *state)
+{
+       u32 an, ctrl0, ctrl2, ctrl4;
+
+       an = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       ctrl0 = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
+       ctrl2 = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
+       ctrl4 = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
+
+       /* Force link down */
+       an &= ~MVPP2_GMAC_FORCE_LINK_PASS;
+       an |= MVPP2_GMAC_FORCE_LINK_DOWN;
+       writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+
+       /* Set the GMAC in a reset state */
+       ctrl2 |= MVPP2_GMAC_PORT_RESET_MASK;
+       writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
+
+       an &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | MVPP2_GMAC_CONFIG_GMII_SPEED |
+               MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FC_ADV_EN |
+               MVPP2_GMAC_FC_ADV_ASM_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG |
+               MVPP2_GMAC_CONFIG_FULL_DUPLEX | MVPP2_GMAC_AN_DUPLEX_EN |
+               MVPP2_GMAC_FORCE_LINK_DOWN);
+       ctrl0 &= ~MVPP2_GMAC_PORT_TYPE_MASK;
+       ctrl2 &= ~(MVPP2_GMAC_PORT_RESET_MASK | MVPP2_GMAC_PCS_ENABLE_MASK);
+
+       if (state->interface == PHY_INTERFACE_MODE_1000BASEX ||
+           state->interface == PHY_INTERFACE_MODE_2500BASEX) {
+               /* 1000BaseX and 2500BaseX ports cannot negotiate speed nor can
+                * they negotiate duplex: they are always operating with a fixed
+                * speed of 1000/2500Mbps in full duplex, so force 1000/2500
+                * speed and full duplex here.
+                */
+               ctrl0 |= MVPP2_GMAC_PORT_TYPE_MASK;
+               an |= MVPP2_GMAC_CONFIG_GMII_SPEED |
+                     MVPP2_GMAC_CONFIG_FULL_DUPLEX;
+       } else if (!phy_interface_mode_is_rgmii(state->interface)) {
+               an |= MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG;
+       }
+
+       if (state->duplex)
+               an |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
+       if (phylink_test(state->advertising, Pause))
+               an |= MVPP2_GMAC_FC_ADV_EN;
+       if (phylink_test(state->advertising, Asym_Pause))
+               an |= MVPP2_GMAC_FC_ADV_ASM_EN;
+
+       if (state->interface == PHY_INTERFACE_MODE_SGMII ||
+           state->interface == PHY_INTERFACE_MODE_1000BASEX ||
+           state->interface == PHY_INTERFACE_MODE_2500BASEX) {
+               an |= MVPP2_GMAC_IN_BAND_AUTONEG;
+               ctrl2 |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK;
+
+               ctrl4 &= ~(MVPP22_CTRL4_EXT_PIN_GMII_SEL |
+                          MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN);
+               ctrl4 |= MVPP22_CTRL4_SYNC_BYPASS_DIS |
+                        MVPP22_CTRL4_DP_CLK_SEL |
+                        MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
+
+               if (state->pause & MLO_PAUSE_TX)
+                       ctrl4 |= MVPP22_CTRL4_TX_FC_EN;
+               if (state->pause & MLO_PAUSE_RX)
+                       ctrl4 |= MVPP22_CTRL4_RX_FC_EN;
+       } else if (phy_interface_mode_is_rgmii(state->interface)) {
+               an |= MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS;
+
+               if (state->speed == SPEED_1000)
+                       an |= MVPP2_GMAC_CONFIG_GMII_SPEED;
+               else if (state->speed == SPEED_100)
+                       an |= MVPP2_GMAC_CONFIG_MII_SPEED;
+
+               ctrl4 &= ~MVPP22_CTRL4_DP_CLK_SEL;
+               ctrl4 |= MVPP22_CTRL4_EXT_PIN_GMII_SEL |
+                        MVPP22_CTRL4_SYNC_BYPASS_DIS |
+                        MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
+       }
+
+       writel(ctrl0, port->base + MVPP2_GMAC_CTRL_0_REG);
+       writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
+       writel(ctrl4, port->base + MVPP22_GMAC_CTRL_4_REG);
+       writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+}
+
+static void mvpp2_mac_config(struct net_device *dev, unsigned int mode,
+                            const struct phylink_link_state *state)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       /* Check for invalid configuration */
+       if (state->interface == PHY_INTERFACE_MODE_10GKR && port->gop_id != 0) {
+               netdev_err(dev, "Invalid mode on %s\n", dev->name);
+               return;
+       }
+
+       netif_tx_stop_all_queues(port->dev);
+       if (!port->has_phy)
+               netif_carrier_off(port->dev);
+
+       /* Make sure the port is disabled when reconfiguring the mode */
+       mvpp2_port_disable(port);
+
+       if (port->priv->hw_version == MVPP22 &&
+           port->phy_interface != state->interface) {
+               port->phy_interface = state->interface;
+
+               /* Reconfigure the serdes lanes */
+               phy_power_off(port->comphy);
+               mvpp22_mode_reconfigure(port);
+       }
+
+       /* mac (re)configuration */
+       if (state->interface == PHY_INTERFACE_MODE_10GKR)
+               mvpp2_xlg_config(port, mode, state);
+       else if (phy_interface_mode_is_rgmii(state->interface) ||
+                state->interface == PHY_INTERFACE_MODE_SGMII ||
+                state->interface == PHY_INTERFACE_MODE_1000BASEX ||
+                state->interface == PHY_INTERFACE_MODE_2500BASEX)
+               mvpp2_gmac_config(port, mode, state);
+
+       if (port->priv->hw_version == MVPP21 && port->flags & MVPP2_F_LOOPBACK)
+               mvpp2_port_loopback_set(port, state);
+
+       /* If the port already was up, make sure it's still in the same state */
+       if (state->link || !port->has_phy) {
+               mvpp2_port_enable(port);
+
+               mvpp2_egress_enable(port);
+               mvpp2_ingress_enable(port);
+               if (!port->has_phy)
+                       netif_carrier_on(dev);
+               netif_tx_wake_all_queues(dev);
+       }
+}
+
+static void mvpp2_mac_link_up(struct net_device *dev, unsigned int mode,
+                             phy_interface_t interface, struct phy_device *phy)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+       u32 val;
+
+       if (!phylink_autoneg_inband(mode) &&
+           interface != PHY_INTERFACE_MODE_10GKR) {
+               val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+               val &= ~MVPP2_GMAC_FORCE_LINK_DOWN;
+               if (phy_interface_mode_is_rgmii(interface))
+                       val |= MVPP2_GMAC_FORCE_LINK_PASS;
+               writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       }
+
+       mvpp2_port_enable(port);
+
+       mvpp2_egress_enable(port);
+       mvpp2_ingress_enable(port);
+       netif_tx_wake_all_queues(dev);
+}
+
+static void mvpp2_mac_link_down(struct net_device *dev, unsigned int mode,
+                               phy_interface_t interface)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+       u32 val;
+
+       if (!phylink_autoneg_inband(mode) &&
+           interface != PHY_INTERFACE_MODE_10GKR) {
+               val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+               val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
+               val |= MVPP2_GMAC_FORCE_LINK_DOWN;
+               writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       }
+
+       netif_tx_stop_all_queues(dev);
+       mvpp2_egress_disable(port);
+       mvpp2_ingress_disable(port);
+
+       /* When using link interrupts to notify phylink of a MAC state change,
+        * we do not want the port to be disabled (we want to receive further
+        * interrupts, to be notified when the port will have a link later).
+        */
+       if (!port->has_phy)
+               return;
+
+       mvpp2_port_disable(port);
+}
+
+static const struct phylink_mac_ops mvpp2_phylink_ops = {
+       .validate = mvpp2_phylink_validate,
+       .mac_link_state = mvpp2_phylink_mac_link_state,
+       .mac_an_restart = mvpp2_mac_an_restart,
+       .mac_config = mvpp2_mac_config,
+       .mac_link_up = mvpp2_mac_link_up,
+       .mac_link_down = mvpp2_mac_link_down,
+};
+
 /* Ports initialization */
 static int mvpp2_port_probe(struct platform_device *pdev,
                            struct fwnode_handle *port_fwnode,
                            struct mvpp2 *priv)
 {
-       struct device_node *phy_node;
        struct phy *comphy = NULL;
        struct mvpp2_port *port;
        struct mvpp2_port_pcpu *port_pcpu;
        struct device_node *port_node = to_of_node(port_fwnode);
        struct net_device *dev;
        struct resource *res;
+       struct phylink *phylink;
        char *mac_from = "";
        unsigned int ntxqs, nrxqs;
        bool has_tx_irqs;
@@ -8212,11 +8406,6 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        if (!dev)
                return -ENOMEM;
 
-       if (port_node)
-               phy_node = of_parse_phandle(port_node, "phy", 0);
-       else
-               phy_node = NULL;
-
        phy_mode = fwnode_get_phy_mode(port_fwnode);
        if (phy_mode < 0) {
                dev_err(&pdev->dev, "incorrect phy mode\n");
@@ -8249,6 +8438,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        port = netdev_priv(dev);
        port->dev = dev;
        port->fwnode = port_fwnode;
+       port->has_phy = !!of_find_property(port_node, "phy", NULL);
        port->ntxqs = ntxqs;
        port->nrxqs = nrxqs;
        port->priv = priv;
@@ -8279,7 +8469,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        else
                port->first_rxq = port->id * priv->max_port_rxqs;
 
-       port->phy_node = phy_node;
+       port->of_node = port_node;
        port->phy_interface = phy_mode;
        port->comphy = comphy;
 
@@ -8340,9 +8530,6 @@ static int mvpp2_port_probe(struct platform_device *pdev,
 
        mvpp2_port_periodic_xon_disable(port);
 
-       if (priv->hw_version == MVPP21)
-               mvpp2_port_fc_adv_enable(port);
-
        mvpp2_port_reset(port);
 
        port->pcpu = alloc_percpu(struct mvpp2_port_pcpu);
@@ -8386,10 +8573,23 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        /* 9704 == 9728 - 20 and rounding to 8 */
        dev->max_mtu = MVPP2_BM_JUMBO_PKT_SIZE;
 
+       /* Phylink isn't used w/ ACPI as of now */
+       if (port_node) {
+               phylink = phylink_create(dev, port_fwnode, phy_mode,
+                                        &mvpp2_phylink_ops);
+               if (IS_ERR(phylink)) {
+                       err = PTR_ERR(phylink);
+                       goto err_free_port_pcpu;
+               }
+               port->phylink = phylink;
+       } else {
+               port->phylink = NULL;
+       }
+
        err = register_netdev(dev);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to register netdev\n");
-               goto err_free_port_pcpu;
+               goto err_phylink;
        }
        netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr);
 
@@ -8397,6 +8597,9 @@ static int mvpp2_port_probe(struct platform_device *pdev,
 
        return 0;
 
+err_phylink:
+       if (port->phylink)
+               phylink_destroy(port->phylink);
 err_free_port_pcpu:
        free_percpu(port->pcpu);
 err_free_txq_pcpu:
@@ -8410,7 +8613,6 @@ err_free_irq:
 err_deinit_qvecs:
        mvpp2_queue_vectors_deinit(port);
 err_free_netdev:
-       of_node_put(phy_node);
        free_netdev(dev);
        return err;
 }
@@ -8421,7 +8623,8 @@ static void mvpp2_port_remove(struct mvpp2_port *port)
        int i;
 
        unregister_netdev(port->dev);
-       of_node_put(port->phy_node);
+       if (port->phylink)
+               phylink_destroy(port->phylink);
        free_percpu(port->pcpu);
        free_percpu(port->stats);
        for (i = 0; i < port->ntxqs; i++)
index 80a75c8..0a30d81 100644 (file)
@@ -2932,6 +2932,7 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port)
                mlx4_err(dev, "Failed to create file for port %d\n", port);
                devlink_port_unregister(&info->devlink_port);
                info->port = -1;
+               return err;
        }
 
        sprintf(info->dev_mtu_name, "mlx4_port%d_mtu", port);
@@ -2953,9 +2954,10 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port)
                                   &info->port_attr);
                devlink_port_unregister(&info->devlink_port);
                info->port = -1;
+               return err;
        }
 
-       return err;
+       return 0;
 }
 
 static void mlx4_cleanup_port_info(struct mlx4_port_info *info)
index d93ff56..b3820a3 100644 (file)
@@ -235,7 +235,7 @@ const char *parse_fs_dst(struct trace_seq *p,
 
        switch (dst->type) {
        case MLX5_FLOW_DESTINATION_TYPE_VPORT:
-               trace_seq_printf(p, "vport=%u\n", dst->vport_num);
+               trace_seq_printf(p, "vport=%u\n", dst->vport.num);
                break;
        case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE:
                trace_seq_printf(p, "ft=%p\n", dst->ft);
index 9cc07da..bc91a73 100644 (file)
@@ -288,8 +288,6 @@ enum {
        MLX5E_RQ_STATE_AM,
 };
 
-#define MLX5E_TEST_BIT(state, nr) (state & BIT(nr))
-
 struct mlx5e_cq {
        /* data path - accessed per cqe */
        struct mlx5_cqwq           wq;
@@ -636,7 +634,6 @@ struct mlx5e_flow_table {
 struct mlx5e_tc_table {
        struct mlx5_flow_table          *t;
 
-       struct rhashtable_params        ht_params;
        struct rhashtable               ht;
 
        DECLARE_HASHTABLE(mod_hdr_tbl, 8);
@@ -1120,9 +1117,6 @@ int mlx5e_ethtool_get_ts_info(struct mlx5e_priv *priv,
 int mlx5e_ethtool_flash_device(struct mlx5e_priv *priv,
                               struct ethtool_flash *flash);
 
-int mlx5e_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
-                           void *cb_priv);
-
 /* mlx5e generic netdev management API */
 struct net_device*
 mlx5e_create_netdev(struct mlx5_core_dev *mdev, const struct mlx5e_profile *profile,
index 68fcb40..f20074d 100644 (file)
@@ -49,7 +49,7 @@ static inline struct sk_buff *mlx5e_accel_handle_tx(struct sk_buff *skb,
                                                    u16 *pi)
 {
 #ifdef CONFIG_MLX5_EN_TLS
-       if (sq->state & BIT(MLX5E_SQ_STATE_TLS)) {
+       if (test_bit(MLX5E_SQ_STATE_TLS, &sq->state)) {
                skb = mlx5e_tls_handle_tx_skb(dev, sq, skb, wqe, pi);
                if (unlikely(!skb))
                        return NULL;
@@ -57,7 +57,7 @@ static inline struct sk_buff *mlx5e_accel_handle_tx(struct sk_buff *skb,
 #endif
 
 #ifdef CONFIG_MLX5_EN_IPSEC
-       if (sq->state & BIT(MLX5E_SQ_STATE_IPSEC)) {
+       if (test_bit(MLX5E_SQ_STATE_IPSEC, &sq->state)) {
                skb = mlx5e_ipsec_handle_tx_skb(dev, *wqe, skb);
                if (unlikely(!skb))
                        return NULL;
index f64dda2..76cc10e 100644 (file)
@@ -277,7 +277,6 @@ static void mlx5e_del_vlan_rule(struct mlx5e_priv *priv,
                }
                break;
        case MLX5E_VLAN_RULE_TYPE_MATCH_CTAG_VID:
-               mlx5e_vport_context_update_vlans(priv);
                if (priv->fs.vlan.active_cvlans_rule[vid]) {
                        mlx5_del_flow_rules(priv->fs.vlan.active_cvlans_rule[vid]);
                        priv->fs.vlan.active_cvlans_rule[vid] = NULL;
index 0f2b66b..b5a7580 100644 (file)
@@ -747,23 +747,24 @@ static void mlx5e_destroy_rq(struct mlx5e_rq *rq)
        mlx5_core_destroy_rq(rq->mdev, rq->rqn);
 }
 
-static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq)
+static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time)
 {
-       unsigned long exp_time = jiffies + msecs_to_jiffies(20000);
+       unsigned long exp_time = jiffies + msecs_to_jiffies(wait_time);
        struct mlx5e_channel *c = rq->channel;
 
        struct mlx5_wq_ll *wq = &rq->wq;
        u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5_wq_ll_get_size(wq));
 
-       while (time_before(jiffies, exp_time)) {
+       do {
                if (wq->cur_sz >= min_wqes)
                        return 0;
 
                msleep(20);
-       }
+       } while (time_before(jiffies, exp_time));
+
+       netdev_warn(c->netdev, "Failed to get min RX wqes on Channel[%d] RQN[0x%x] wq cur_sz(%d) min_rx_wqes(%d)\n",
+                   c->ix, rq->rqn, wq->cur_sz, min_wqes);
 
-       netdev_warn(c->netdev, "Failed to get min RX wqes on RQN[0x%x] wq cur_sz(%d) min_rx_wqes(%d)\n",
-                   rq->rqn, wq->cur_sz, min_wqes);
        return -ETIMEDOUT;
 }
 
@@ -819,7 +820,7 @@ static int mlx5e_open_rq(struct mlx5e_channel *c,
                goto err_destroy_rq;
 
        if (params->rx_dim_enabled)
-               c->rq.state |= BIT(MLX5E_RQ_STATE_AM);
+               __set_bit(MLX5E_RQ_STATE_AM, &c->rq.state);
 
        return 0;
 
@@ -2128,13 +2129,11 @@ static int mlx5e_wait_channels_min_rx_wqes(struct mlx5e_channels *chs)
        int err = 0;
        int i;
 
-       for (i = 0; i < chs->num; i++) {
-               err = mlx5e_wait_for_min_rx_wqes(&chs->c[i]->rq);
-               if (err)
-                       break;
-       }
+       for (i = 0; i < chs->num; i++)
+               err |= mlx5e_wait_for_min_rx_wqes(&chs->c[i]->rq,
+                                                 err ? 0 : 20000);
 
-       return err;
+       return err ? -ETIMEDOUT : 0;
 }
 
 static void mlx5e_deactivate_channels(struct mlx5e_channels *chs)
@@ -3137,22 +3136,23 @@ out:
 
 #ifdef CONFIG_MLX5_ESWITCH
 static int mlx5e_setup_tc_cls_flower(struct mlx5e_priv *priv,
-                                    struct tc_cls_flower_offload *cls_flower)
+                                    struct tc_cls_flower_offload *cls_flower,
+                                    int flags)
 {
        switch (cls_flower->command) {
        case TC_CLSFLOWER_REPLACE:
-               return mlx5e_configure_flower(priv, cls_flower);
+               return mlx5e_configure_flower(priv, cls_flower, flags);
        case TC_CLSFLOWER_DESTROY:
-               return mlx5e_delete_flower(priv, cls_flower);
+               return mlx5e_delete_flower(priv, cls_flower, flags);
        case TC_CLSFLOWER_STATS:
-               return mlx5e_stats_flower(priv, cls_flower);
+               return mlx5e_stats_flower(priv, cls_flower, flags);
        default:
                return -EOPNOTSUPP;
        }
 }
 
-int mlx5e_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
-                           void *cb_priv)
+static int mlx5e_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
+                                  void *cb_priv)
 {
        struct mlx5e_priv *priv = cb_priv;
 
@@ -3161,7 +3161,7 @@ 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);
+               return mlx5e_setup_tc_cls_flower(priv, type_data, MLX5E_TC_INGRESS);
        default:
                return -EOPNOTSUPP;
        }
@@ -4462,7 +4462,7 @@ static int mlx5e_init_nic_rx(struct mlx5e_priv *priv)
                goto err_destroy_direct_tirs;
        }
 
-       err = mlx5e_tc_init(priv);
+       err = mlx5e_tc_nic_init(priv);
        if (err)
                goto err_destroy_flow_steering;
 
@@ -4483,7 +4483,7 @@ err_destroy_indirect_rqts:
 
 static void mlx5e_cleanup_nic_rx(struct mlx5e_priv *priv)
 {
-       mlx5e_tc_cleanup(priv);
+       mlx5e_tc_nic_cleanup(priv);
        mlx5e_destroy_flow_steering(priv);
        mlx5e_destroy_direct_tirs(priv);
        mlx5e_destroy_indirect_tirs(priv);
index 876c3e4..c3034f5 100644 (file)
@@ -66,18 +66,36 @@ static const struct counter_desc sw_rep_stats_desc[] = {
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_bytes) },
 };
 
-#define NUM_VPORT_REP_COUNTERS ARRAY_SIZE(sw_rep_stats_desc)
+struct vport_stats {
+       u64 vport_rx_packets;
+       u64 vport_tx_packets;
+       u64 vport_rx_bytes;
+       u64 vport_tx_bytes;
+};
+
+static const struct counter_desc vport_rep_stats_desc[] = {
+       { MLX5E_DECLARE_STAT(struct vport_stats, vport_rx_packets) },
+       { MLX5E_DECLARE_STAT(struct vport_stats, vport_rx_bytes) },
+       { MLX5E_DECLARE_STAT(struct vport_stats, vport_tx_packets) },
+       { MLX5E_DECLARE_STAT(struct vport_stats, vport_tx_bytes) },
+};
+
+#define NUM_VPORT_REP_SW_COUNTERS ARRAY_SIZE(sw_rep_stats_desc)
+#define NUM_VPORT_REP_HW_COUNTERS ARRAY_SIZE(vport_rep_stats_desc)
 
 static void mlx5e_rep_get_strings(struct net_device *dev,
                                  u32 stringset, uint8_t *data)
 {
-       int i;
+       int i, j;
 
        switch (stringset) {
        case ETH_SS_STATS:
-               for (i = 0; i < NUM_VPORT_REP_COUNTERS; i++)
+               for (i = 0; i < NUM_VPORT_REP_SW_COUNTERS; i++)
                        strcpy(data + (i * ETH_GSTRING_LEN),
                               sw_rep_stats_desc[i].format);
+               for (j = 0; j < NUM_VPORT_REP_HW_COUNTERS; j++, i++)
+                       strcpy(data + (i * ETH_GSTRING_LEN),
+                              vport_rep_stats_desc[j].format);
                break;
        }
 }
@@ -140,7 +158,7 @@ static void mlx5e_rep_get_ethtool_stats(struct net_device *dev,
                                        struct ethtool_stats *stats, u64 *data)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
-       int i;
+       int i, j;
 
        if (!data)
                return;
@@ -148,18 +166,23 @@ static void mlx5e_rep_get_ethtool_stats(struct net_device *dev,
        mutex_lock(&priv->state_lock);
        if (test_bit(MLX5E_STATE_OPENED, &priv->state))
                mlx5e_rep_update_sw_counters(priv);
+       mlx5e_rep_update_hw_counters(priv);
        mutex_unlock(&priv->state_lock);
 
-       for (i = 0; i < NUM_VPORT_REP_COUNTERS; i++)
+       for (i = 0; i < NUM_VPORT_REP_SW_COUNTERS; i++)
                data[i] = MLX5E_READ_CTR64_CPU(&priv->stats.sw,
                                               sw_rep_stats_desc, i);
+
+       for (j = 0; j < NUM_VPORT_REP_HW_COUNTERS; j++, i++)
+               data[i] = MLX5E_READ_CTR64_CPU(&priv->stats.vf_vport,
+                                              vport_rep_stats_desc, j);
 }
 
 static int mlx5e_rep_get_sset_count(struct net_device *dev, int sset)
 {
        switch (sset) {
        case ETH_SS_STATS:
-               return NUM_VPORT_REP_COUNTERS;
+               return NUM_VPORT_REP_SW_COUNTERS + NUM_VPORT_REP_HW_COUNTERS;
        default:
                return -EOPNOTSUPP;
        }
@@ -681,8 +704,8 @@ static int mlx5e_rep_open(struct net_device *dev)
                goto unlock;
 
        if (!mlx5_modify_vport_admin_state(priv->mdev,
-                       MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
-                       rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_UP))
+                                          MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+                                          rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_UP))
                netif_carrier_on(dev);
 
 unlock:
@@ -699,8 +722,8 @@ static int mlx5e_rep_close(struct net_device *dev)
 
        mutex_lock(&priv->state_lock);
        mlx5_modify_vport_admin_state(priv->mdev,
-                       MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
-                       rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN);
+                                     MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+                                     rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN);
        ret = mlx5e_close_locked(dev);
        mutex_unlock(&priv->state_lock);
        return ret;
@@ -723,15 +746,31 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev,
 
 static int
 mlx5e_rep_setup_tc_cls_flower(struct mlx5e_priv *priv,
-                             struct tc_cls_flower_offload *cls_flower)
+                             struct tc_cls_flower_offload *cls_flower, int flags)
 {
        switch (cls_flower->command) {
        case TC_CLSFLOWER_REPLACE:
-               return mlx5e_configure_flower(priv, cls_flower);
+               return mlx5e_configure_flower(priv, cls_flower, flags);
        case TC_CLSFLOWER_DESTROY:
-               return mlx5e_delete_flower(priv, cls_flower);
+               return mlx5e_delete_flower(priv, cls_flower, flags);
        case TC_CLSFLOWER_STATS:
-               return mlx5e_stats_flower(priv, cls_flower);
+               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;
+
+       if (!tc_cls_can_offload_and_chain0(priv->netdev, type_data))
+               return -EOPNOTSUPP;
+
+       switch (type) {
+       case TC_SETUP_CLSFLOWER:
+               return mlx5e_rep_setup_tc_cls_flower(priv, type_data, MLX5E_TC_EGRESS);
        default:
                return -EOPNOTSUPP;
        }
@@ -747,7 +786,7 @@ 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);
+               return mlx5e_rep_setup_tc_cls_flower(priv, type_data, MLX5E_TC_INGRESS);
        default:
                return -EOPNOTSUPP;
        }
@@ -965,14 +1004,8 @@ static int mlx5e_init_rep_rx(struct mlx5e_priv *priv)
        }
        rpriv->vport_rx_rule = flow_rule;
 
-       err = mlx5e_tc_init(priv);
-       if (err)
-               goto err_del_flow_rule;
-
        return 0;
 
-err_del_flow_rule:
-       mlx5_del_flow_rules(rpriv->vport_rx_rule);
 err_destroy_direct_tirs:
        mlx5e_destroy_direct_tirs(priv);
 err_destroy_direct_rqts:
@@ -984,7 +1017,6 @@ static void mlx5e_cleanup_rep_rx(struct mlx5e_priv *priv)
 {
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
 
-       mlx5e_tc_cleanup(priv);
        mlx5_del_flow_rules(rpriv->vport_rx_rule);
        mlx5e_destroy_direct_tirs(priv);
        mlx5e_destroy_direct_rqts(priv);
@@ -1042,8 +1074,15 @@ mlx5e_nic_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
        if (err)
                goto err_remove_sqs;
 
+       /* init shared tc flow table */
+       err = mlx5e_tc_esw_init(&rpriv->tc_ht);
+       if (err)
+               goto  err_neigh_cleanup;
+
        return 0;
 
+err_neigh_cleanup:
+       mlx5e_rep_neigh_cleanup(rpriv);
 err_remove_sqs:
        mlx5e_remove_sqs_fwd_rules(priv);
        return err;
@@ -1058,9 +1097,8 @@ mlx5e_nic_rep_unload(struct mlx5_eswitch_rep *rep)
        if (test_bit(MLX5E_STATE_OPENED, &priv->state))
                mlx5e_remove_sqs_fwd_rules(priv);
 
-       /* clean (and re-init) existing uplink offloaded TC rules */
-       mlx5e_tc_cleanup(priv);
-       mlx5e_tc_init(priv);
+       /* clean uplink offloaded TC rules, delete shared tc flow table */
+       mlx5e_tc_esw_cleanup(&rpriv->tc_ht);
 
        mlx5e_rep_neigh_cleanup(rpriv);
 }
@@ -1107,7 +1145,7 @@ mlx5e_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
 
        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_setup_tc_block_cb,
+       err = tc_setup_cb_egdev_register(netdev, mlx5e_rep_setup_tc_cb_egdev,
                                         upriv);
        if (err)
                goto err_neigh_cleanup;
@@ -1122,7 +1160,7 @@ mlx5e_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
        return 0;
 
 err_egdev_cleanup:
-       tc_setup_cb_egdev_unregister(netdev, mlx5e_setup_tc_block_cb,
+       tc_setup_cb_egdev_unregister(netdev, mlx5e_rep_setup_tc_cb_egdev,
                                     upriv);
 
 err_neigh_cleanup:
@@ -1151,7 +1189,7 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch_rep *rep)
        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_setup_tc_block_cb,
+       tc_setup_cb_egdev_unregister(netdev, mlx5e_rep_setup_tc_cb_egdev,
                                     upriv);
        mlx5e_rep_neigh_cleanup(rpriv);
        mlx5e_detach_netdev(priv);
index b9b481f..844d32d 100644 (file)
@@ -59,6 +59,7 @@ struct mlx5e_rep_priv {
        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 */
 };
 
 static inline
index 7bbf0db..53f7292 100644 (file)
@@ -450,7 +450,7 @@ bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq)
        struct mlx5_wq_ll *wq = &rq->wq;
        int err;
 
-       if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED)))
+       if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state)))
                return false;
 
        if (mlx5_wq_ll_is_full(wq))
@@ -508,7 +508,7 @@ static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq, struct mlx5e_rq *rq)
        struct mlx5e_icosq *sq = container_of(cq, struct mlx5e_icosq, cq);
        struct mlx5_cqe64 *cqe;
 
-       if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED)))
+       if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state)))
                return;
 
        cqe = mlx5_cqwq_get_cqe(&cq->wq);
@@ -525,7 +525,7 @@ bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq)
 {
        struct mlx5_wq_ll *wq = &rq->wq;
 
-       if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED)))
+       if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state)))
                return false;
 
        mlx5e_poll_ico_cq(&rq->channel->icosq.cq, rq);
@@ -681,11 +681,10 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe,
                                      struct mlx5e_rq *rq,
                                      struct sk_buff *skb)
 {
+       u8 lro_num_seg = be32_to_cpu(cqe->srqn) >> 24;
        struct net_device *netdev = rq->netdev;
-       int lro_num_seg;
 
        skb->mac_len = ETH_HLEN;
-       lro_num_seg = be32_to_cpu(cqe->srqn) >> 24;
        if (lro_num_seg > 1) {
                mlx5e_lro_update_hdr(skb, cqe, cqe_bcnt);
                skb_shinfo(skb)->gso_size = DIV_ROUND_UP(cqe_bcnt, lro_num_seg);
@@ -808,9 +807,9 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq,
 }
 
 /* returns true if packet was consumed by xdp */
-static inline int mlx5e_xdp_handle(struct mlx5e_rq *rq,
-                                  struct mlx5e_dma_info *di,
-                                  void *va, u16 *rx_headroom, u32 *len)
+static inline bool mlx5e_xdp_handle(struct mlx5e_rq *rq,
+                                   struct mlx5e_dma_info *di,
+                                   void *va, u16 *rx_headroom, u32 *len)
 {
        struct bpf_prog *prog = READ_ONCE(rq->xdp_prog);
        struct xdp_buff xdp;
@@ -1133,7 +1132,7 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
        struct mlx5_cqe64 *cqe;
        int work_done = 0;
 
-       if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED)))
+       if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state)))
                return 0;
 
        if (cq->decmprs_left)
@@ -1186,7 +1185,7 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq)
 
        sq = container_of(cq, struct mlx5e_xdpsq, cq);
 
-       if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED)))
+       if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state)))
                return false;
 
        cqe = mlx5_cqwq_get_cqe(&cq->wq);
index b94276d..674f1d7 100644 (file)
@@ -58,19 +58,25 @@ struct mlx5_nic_flow_attr {
        u32 flow_tag;
        u32 mod_hdr_id;
        u32 hairpin_tirn;
+       u8 match_level;
        struct mlx5_flow_table  *hairpin_ft;
 };
 
+#define MLX5E_TC_FLOW_BASE (MLX5E_TC_LAST_EXPORTED_BIT + 1)
+
 enum {
-       MLX5E_TC_FLOW_ESWITCH   = BIT(0),
-       MLX5E_TC_FLOW_NIC       = BIT(1),
-       MLX5E_TC_FLOW_OFFLOADED = BIT(2),
-       MLX5E_TC_FLOW_HAIRPIN   = BIT(3),
-       MLX5E_TC_FLOW_HAIRPIN_RSS = BIT(4),
+       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),
 };
 
 struct mlx5e_tc_flow {
        struct rhash_head       node;
+       struct mlx5e_priv       *priv;
        u64                     cookie;
        u8                      flags;
        struct mlx5_flow_handle *rule;
@@ -97,7 +103,7 @@ enum {
 };
 
 #define MLX5E_TC_TABLE_NUM_GROUPS 4
-#define MLX5E_TC_TABLE_MAX_GROUP_SIZE (1 << 16)
+#define MLX5E_TC_TABLE_MAX_GROUP_SIZE BIT(16)
 
 struct mlx5e_hairpin {
        struct mlx5_hairpin *pair;
@@ -753,7 +759,9 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
                table_created = true;
        }
 
-       parse_attr->spec.match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+       if (attr->match_level != MLX5_MATCH_NONE)
+               parse_attr->spec.match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+
        rule = mlx5_add_flow_rules(priv->fs.tc.t, &parse_attr->spec,
                                   &flow_act, dest, dest_ix);
 
@@ -789,7 +797,7 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
        mlx5_del_flow_rules(flow->rule);
        mlx5_fc_destroy(priv->mdev, counter);
 
-       if (!mlx5e_tc_num_filters(priv) && (priv->fs.tc.t)) {
+       if (!mlx5e_tc_num_filters(priv) && priv->fs.tc.t) {
                mlx5_destroy_flow_table(priv->fs.tc.t);
                priv->fs.tc.t = NULL;
        }
@@ -836,6 +844,7 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
                out_priv = netdev_priv(encap_dev);
                rpriv = out_priv->ppriv;
                attr->out_rep = rpriv->rep;
+               attr->out_mdev = out_priv->mdev;
        }
 
        err = mlx5_eswitch_add_vlan_action(esw, attr);
@@ -982,6 +991,8 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
                                }
                        }
                }
+               if (neigh_used)
+                       break;
        }
 
        if (neigh_used) {
@@ -1190,7 +1201,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,
-                             u8 *min_inline)
+                             u8 *match_level)
 {
        void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
                                       outer_headers);
@@ -1199,7 +1210,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
        u16 addr_type = 0;
        u8 ip_proto = 0;
 
-       *min_inline = MLX5_INLINE_MODE_L2;
+       *match_level = MLX5_MATCH_NONE;
 
        if (f->dissector->used_keys &
            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
@@ -1249,58 +1260,6 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                                         inner_headers);
        }
 
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
-               struct flow_dissector_key_control *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_CONTROL,
-                                                 f->key);
-
-               struct flow_dissector_key_control *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_CONTROL,
-                                                 f->mask);
-               addr_type = key->addr_type;
-
-               /* the HW doesn't support frag first/later */
-               if (mask->flags & FLOW_DIS_FIRST_FRAG)
-                       return -EOPNOTSUPP;
-
-               if (mask->flags & FLOW_DIS_IS_FRAGMENT) {
-                       MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1);
-                       MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag,
-                                key->flags & FLOW_DIS_IS_FRAGMENT);
-
-                       /* the HW doesn't need L3 inline to match on frag=no */
-                       if (key->flags & FLOW_DIS_IS_FRAGMENT)
-                               *min_inline = MLX5_INLINE_MODE_IP;
-               }
-       }
-
-       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
-               struct flow_dissector_key_basic *key =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->key);
-               struct flow_dissector_key_basic *mask =
-                       skb_flow_dissector_target(f->dissector,
-                                                 FLOW_DISSECTOR_KEY_BASIC,
-                                                 f->mask);
-               ip_proto = key->ip_proto;
-
-               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));
-
-               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
-                        mask->ip_proto);
-               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
-                        key->ip_proto);
-
-               if (mask->ip_proto)
-                       *min_inline = MLX5_INLINE_MODE_IP;
-       }
-
        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
                struct flow_dissector_key_eth_addrs *key =
                        skb_flow_dissector_target(f->dissector,
@@ -1324,6 +1283,9 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                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;
        }
 
        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
@@ -1344,9 +1306,79 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
 
                        MLX5_SET(fte_match_set_lyr_2_4, headers_c, first_prio, mask->vlan_priority);
                        MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_prio, key->vlan_priority);
+
+                       *match_level = MLX5_MATCH_L2;
                }
        }
 
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_dissector_key_basic *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_BASIC,
+                                                 f->key);
+               struct flow_dissector_key_basic *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_BASIC,
+                                                 f->mask);
+               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)
+                       *match_level = MLX5_MATCH_L2;
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
+               struct flow_dissector_key_control *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_CONTROL,
+                                                 f->key);
+
+               struct flow_dissector_key_control *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_CONTROL,
+                                                 f->mask);
+               addr_type = key->addr_type;
+
+               /* the HW doesn't support frag first/later */
+               if (mask->flags & FLOW_DIS_FIRST_FRAG)
+                       return -EOPNOTSUPP;
+
+               if (mask->flags & FLOW_DIS_IS_FRAGMENT) {
+                       MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1);
+                       MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag,
+                                key->flags & FLOW_DIS_IS_FRAGMENT);
+
+                       /* 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;
+       /* ***  L2 attributes parsing up to here *** */
+                       else
+                               *match_level = MLX5_INLINE_MODE_IP;
+               }
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_dissector_key_basic *key =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_BASIC,
+                                                 f->key);
+               struct flow_dissector_key_basic *mask =
+                       skb_flow_dissector_target(f->dissector,
+                                                 FLOW_DISSECTOR_KEY_BASIC,
+                                                 f->mask);
+               ip_proto = key->ip_proto;
+
+               MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
+                        mask->ip_proto);
+               MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
+                        key->ip_proto);
+
+               if (mask->ip_proto)
+                       *match_level = MLX5_MATCH_L3;
+       }
+
        if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
                struct flow_dissector_key_ipv4_addrs *key =
                        skb_flow_dissector_target(f->dissector,
@@ -1371,7 +1403,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                       &key->dst, sizeof(key->dst));
 
                if (mask->src || mask->dst)
-                       *min_inline = MLX5_INLINE_MODE_IP;
+                       *match_level = MLX5_MATCH_L3;
        }
 
        if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
@@ -1400,7 +1432,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
 
                if (ipv6_addr_type(&mask->src) != IPV6_ADDR_ANY ||
                    ipv6_addr_type(&mask->dst) != IPV6_ADDR_ANY)
-                       *min_inline = MLX5_INLINE_MODE_IP;
+                       *match_level = MLX5_MATCH_L3;
        }
 
        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IP)) {
@@ -1428,9 +1460,11 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                        return -EOPNOTSUPP;
 
                if (mask->tos || mask->ttl)
-                       *min_inline = MLX5_INLINE_MODE_IP;
+                       *match_level = MLX5_MATCH_L3;
        }
 
+       /* ***  L3 attributes parsing up to here *** */
+
        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
                struct flow_dissector_key_ports *key =
                        skb_flow_dissector_target(f->dissector,
@@ -1471,7 +1505,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                }
 
                if (mask->src || mask->dst)
-                       *min_inline = MLX5_INLINE_MODE_TCP_UDP;
+                       *match_level = MLX5_MATCH_L4;
        }
 
        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_TCP)) {
@@ -1490,7 +1524,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
                         ntohs(key->flags));
 
                if (mask->flags)
-                       *min_inline = MLX5_INLINE_MODE_TCP_UDP;
+                       *match_level = MLX5_MATCH_L4;
        }
 
        return 0;
@@ -1505,23 +1539,28 @@ static int parse_cls_flower(struct mlx5e_priv *priv,
        struct mlx5_eswitch *esw = dev->priv.eswitch;
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
        struct mlx5_eswitch_rep *rep;
-       u8 min_inline;
+       u8 match_level;
        int err;
 
-       err = __parse_cls_flower(priv, spec, f, &min_inline);
+       err = __parse_cls_flower(priv, spec, f, &match_level);
 
        if (!err && (flow->flags & MLX5E_TC_FLOW_ESWITCH)) {
                rep = rpriv->rep;
                if (rep->vport != FDB_UPLINK_VPORT &&
                    (esw->offloads.inline_mode != MLX5_INLINE_MODE_NONE &&
-                   esw->offloads.inline_mode < min_inline)) {
+                   esw->offloads.inline_mode < match_level)) {
                        netdev_warn(priv->netdev,
                                    "Flow is not offloaded due to min inline setting, required %d actual %d\n",
-                                   min_inline, esw->offloads.inline_mode);
+                                   match_level, esw->offloads.inline_mode);
                        return -EOPNOTSUPP;
                }
        }
 
+       if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
+               flow->esw_attr->match_level = match_level;
+       else
+               flow->nic_attr->match_level = match_level;
+
        return err;
 }
 
@@ -1577,7 +1616,6 @@ struct mlx5_fields {
                {MLX5_ACTION_IN_FIELD_OUT_ ## fw_field, size, offsetof(struct pedit_headers, field) + (off)}
 
 static struct mlx5_fields fields[] = {
-       OFFLOAD(DMAC_47_16, 4, eth.h_dest[0], 0),
        OFFLOAD(DMAC_47_16, 4, eth.h_dest[0], 0),
        OFFLOAD(DMAC_15_0,  2, eth.h_dest[4], 0),
        OFFLOAD(SMAC_47_16, 4, eth.h_source[0], 0),
@@ -1764,12 +1802,12 @@ static int parse_tc_pedit_action(struct mlx5e_priv *priv,
                err = -EOPNOTSUPP; /* can't be all optimistic */
 
                if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
-                       printk(KERN_WARNING "mlx5: legacy pedit isn't offloaded\n");
+                       netdev_warn(priv->netdev, "legacy pedit isn't offloaded\n");
                        goto out_err;
                }
 
                if (cmd != TCA_PEDIT_KEY_EX_CMD_SET && cmd != TCA_PEDIT_KEY_EX_CMD_ADD) {
-                       printk(KERN_WARNING "mlx5: pedit cmd %d isn't offloaded\n", cmd);
+                       netdev_warn(priv->netdev, "pedit cmd %d isn't offloaded\n", cmd);
                        goto out_err;
                }
 
@@ -1793,8 +1831,7 @@ static int parse_tc_pedit_action(struct mlx5e_priv *priv,
        for (cmd = 0; cmd < __PEDIT_CMD_MAX; cmd++) {
                cmd_masks = &masks[cmd];
                if (memcmp(cmd_masks, &zero_masks, sizeof(zero_masks))) {
-                       printk(KERN_WARNING "mlx5: attempt to offload an unsupported field (cmd %d)\n",
-                              cmd);
+                       netdev_warn(priv->netdev, "attempt to offload an unsupported field (cmd %d)\n", cmd);
                        print_hex_dump(KERN_WARNING, "mask: ", DUMP_PREFIX_ADDRESS,
                                       16, 1, cmd_masks, sizeof(zero_masks), true);
                        err = -EOPNOTSUPP;
@@ -1917,21 +1954,21 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
        struct mlx5_nic_flow_attr *attr = flow->nic_attr;
        const struct tc_action *a;
        LIST_HEAD(actions);
+       u32 action = 0;
        int err;
 
        if (!tcf_exts_has_actions(exts))
                return -EINVAL;
 
        attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
-       attr->action = 0;
 
        tcf_exts_to_list(exts, &actions);
        list_for_each_entry(a, &actions, list) {
                if (is_tcf_gact_shot(a)) {
-                       attr->action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
+                       action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
                        if (MLX5_CAP_FLOWTABLE(priv->mdev,
                                               flow_table_properties_nic_receive.flow_counter))
-                               attr->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
+                               action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
                        continue;
                }
 
@@ -1941,13 +1978,13 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                        if (err)
                                return err;
 
-                       attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR |
-                                       MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+                       action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR |
+                                 MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
                        continue;
                }
 
                if (is_tcf_csum(a)) {
-                       if (csum_offload_supported(priv, attr->action,
+                       if (csum_offload_supported(priv, action,
                                                   tcf_csum_update_flags(a)))
                                continue;
 
@@ -1961,8 +1998,8 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                            same_hw_devs(priv, netdev_priv(peer_dev))) {
                                parse_attr->mirred_ifindex = peer_dev->ifindex;
                                flow->flags |= MLX5E_TC_FLOW_HAIRPIN;
-                               attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
-                                               MLX5_FLOW_CONTEXT_ACTION_COUNT;
+                               action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
+                                         MLX5_FLOW_CONTEXT_ACTION_COUNT;
                        } else {
                                netdev_warn(priv->netdev, "device %s not on same HW, can't offload\n",
                                            peer_dev->name);
@@ -1981,13 +2018,14 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                        }
 
                        attr->flow_tag = mark;
-                       attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+                       action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
                        continue;
                }
 
                return -EINVAL;
        }
 
+       attr->action = action;
        if (!actions_match_supported(priv, exts, parse_attr, flow))
                return -EOPNOTSUPP;
 
@@ -2044,6 +2082,20 @@ static int mlx5e_route_lookup_ipv4(struct mlx5e_priv *priv,
        return 0;
 }
 
+static bool is_merged_eswitch_dev(struct mlx5e_priv *priv,
+                                 struct net_device *peer_netdev)
+{
+       struct mlx5e_priv *peer_priv;
+
+       peer_priv = netdev_priv(peer_netdev);
+
+       return (MLX5_CAP_ESW(priv->mdev, merged_eswitch) &&
+               (priv->netdev->netdev_ops == peer_netdev->netdev_ops) &&
+               same_hw_devs(priv, peer_priv) &&
+               MLX5_VPORT_MANAGER(peer_priv->mdev) &&
+               (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,
@@ -2459,34 +2511,36 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
        const struct tc_action *a;
        LIST_HEAD(actions);
        bool encap = false;
-       int err = 0;
+       u32 action = 0;
 
        if (!tcf_exts_has_actions(exts))
                return -EINVAL;
 
-       memset(attr, 0, sizeof(*attr));
        attr->in_rep = rpriv->rep;
+       attr->in_mdev = priv->mdev;
 
        tcf_exts_to_list(exts, &actions);
        list_for_each_entry(a, &actions, list) {
                if (is_tcf_gact_shot(a)) {
-                       attr->action |= MLX5_FLOW_CONTEXT_ACTION_DROP |
-                                       MLX5_FLOW_CONTEXT_ACTION_COUNT;
+                       action |= MLX5_FLOW_CONTEXT_ACTION_DROP |
+                                 MLX5_FLOW_CONTEXT_ACTION_COUNT;
                        continue;
                }
 
                if (is_tcf_pedit(a)) {
+                       int err;
+
                        err = parse_tc_pedit_action(priv, a, MLX5_FLOW_NAMESPACE_FDB,
                                                    parse_attr);
                        if (err)
                                return err;
 
-                       attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
+                       action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
                        continue;
                }
 
                if (is_tcf_csum(a)) {
-                       if (csum_offload_supported(priv, attr->action,
+                       if (csum_offload_supported(priv, action,
                                                   tcf_csum_update_flags(a)))
                                continue;
 
@@ -2500,19 +2554,21 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                        out_dev = tcf_mirred_dev(a);
 
                        if (switchdev_port_same_parent_id(priv->netdev,
-                                                         out_dev)) {
-                               attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
-                                       MLX5_FLOW_CONTEXT_ACTION_COUNT;
+                                                         out_dev) ||
+                           is_merged_eswitch_dev(priv, out_dev)) {
+                               action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
+                                         MLX5_FLOW_CONTEXT_ACTION_COUNT;
                                out_priv = netdev_priv(out_dev);
                                rpriv = out_priv->ppriv;
                                attr->out_rep = rpriv->rep;
+                               attr->out_mdev = out_priv->mdev;
                        } else if (encap) {
                                parse_attr->mirred_ifindex = out_dev->ifindex;
                                parse_attr->tun_info = *info;
                                attr->parse_attr = parse_attr;
-                               attr->action |= MLX5_FLOW_CONTEXT_ACTION_ENCAP |
-                                       MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
-                                       MLX5_FLOW_CONTEXT_ACTION_COUNT;
+                               action |= MLX5_FLOW_CONTEXT_ACTION_ENCAP |
+                                         MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
+                                         MLX5_FLOW_CONTEXT_ACTION_COUNT;
                                /* attr->out_rep is resolved when we handle encap */
                        } else {
                                pr_err("devices %s %s not on same switch HW, can't offload forwarding\n",
@@ -2533,9 +2589,9 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
 
                if (is_tcf_vlan(a)) {
                        if (tcf_vlan_action(a) == TCA_VLAN_ACT_POP) {
-                               attr->action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
+                               action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
                        } else if (tcf_vlan_action(a) == TCA_VLAN_ACT_PUSH) {
-                               attr->action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
+                               action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
                                attr->vlan_vid = tcf_vlan_push_vid(a);
                                if (mlx5_eswitch_vlan_actions_supported(priv->mdev)) {
                                        attr->vlan_prio = tcf_vlan_push_prio(a);
@@ -2553,34 +2609,74 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
                }
 
                if (is_tcf_tunnel_release(a)) {
-                       attr->action |= MLX5_FLOW_CONTEXT_ACTION_DECAP;
+                       action |= MLX5_FLOW_CONTEXT_ACTION_DECAP;
                        continue;
                }
 
                return -EINVAL;
        }
 
+       attr->action = action;
        if (!actions_match_supported(priv, exts, parse_attr, flow))
                return -EOPNOTSUPP;
 
-       return err;
+       return 0;
+}
+
+static void get_flags(int flags, u8 *flow_flags)
+{
+       u8 __flow_flags = 0;
+
+       if (flags & MLX5E_TC_INGRESS)
+               __flow_flags |= MLX5E_TC_FLOW_INGRESS;
+       if (flags & MLX5E_TC_EGRESS)
+               __flow_flags |= MLX5E_TC_FLOW_EGRESS;
+
+       *flow_flags = __flow_flags;
+}
+
+static const struct rhashtable_params tc_ht_params = {
+       .head_offset = offsetof(struct mlx5e_tc_flow, node),
+       .key_offset = offsetof(struct mlx5e_tc_flow, cookie),
+       .key_len = sizeof(((struct mlx5e_tc_flow *)0)->cookie),
+       .automatic_shrinking = true,
+};
+
+static struct rhashtable *get_tc_ht(struct mlx5e_priv *priv)
+{
+       struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+       struct mlx5e_rep_priv *uplink_rpriv;
+
+       if (MLX5_VPORT_MANAGER(priv->mdev) && esw->mode == SRIOV_OFFLOADS) {
+               uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
+               return &uplink_rpriv->tc_ht;
+       } else
+               return &priv->fs.tc.ht;
 }
 
 int mlx5e_configure_flower(struct mlx5e_priv *priv,
-                          struct tc_cls_flower_offload *f)
+                          struct tc_cls_flower_offload *f, int flags)
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        struct mlx5e_tc_flow_parse_attr *parse_attr;
-       struct mlx5e_tc_table *tc = &priv->fs.tc;
+       struct rhashtable *tc_ht = get_tc_ht(priv);
        struct mlx5e_tc_flow *flow;
        int attr_size, err = 0;
        u8 flow_flags = 0;
 
+       get_flags(flags, &flow_flags);
+
+       flow = rhashtable_lookup_fast(tc_ht, &f->cookie, tc_ht_params);
+       if (flow) {
+               netdev_warn_once(priv->netdev, "flow cookie %lx already exists, ignoring\n", f->cookie);
+               return 0;
+       }
+
        if (esw && esw->mode == SRIOV_OFFLOADS) {
-               flow_flags = MLX5E_TC_FLOW_ESWITCH;
+               flow_flags |= MLX5E_TC_FLOW_ESWITCH;
                attr_size  = sizeof(struct mlx5_esw_flow_attr);
        } else {
-               flow_flags = MLX5E_TC_FLOW_NIC;
+               flow_flags |= MLX5E_TC_FLOW_NIC;
                attr_size  = sizeof(struct mlx5_nic_flow_attr);
        }
 
@@ -2593,6 +2689,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv,
 
        flow->cookie = f->cookie;
        flow->flags = flow_flags;
+       flow->priv = priv;
 
        err = parse_cls_flower(priv, flow, &parse_attr->spec, f);
        if (err < 0)
@@ -2623,8 +2720,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv,
            !(flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP))
                kvfree(parse_attr);
 
-       err = rhashtable_insert_fast(&tc->ht, &flow->node,
-                                    tc->ht_params);
+       err = rhashtable_insert_fast(tc_ht, &flow->node, tc_ht_params);
        if (err) {
                mlx5e_tc_del_flow(priv, flow);
                kfree(flow);
@@ -2638,18 +2734,28 @@ err_free:
        return err;
 }
 
+#define DIRECTION_MASK (MLX5E_TC_INGRESS | MLX5E_TC_EGRESS)
+#define FLOW_DIRECTION_MASK (MLX5E_TC_FLOW_INGRESS | MLX5E_TC_FLOW_EGRESS)
+
+static bool same_flow_direction(struct mlx5e_tc_flow *flow, int flags)
+{
+       if ((flow->flags & FLOW_DIRECTION_MASK) == (flags & DIRECTION_MASK))
+               return true;
+
+       return false;
+}
+
 int mlx5e_delete_flower(struct mlx5e_priv *priv,
-                       struct tc_cls_flower_offload *f)
+                       struct tc_cls_flower_offload *f, int flags)
 {
+       struct rhashtable *tc_ht = get_tc_ht(priv);
        struct mlx5e_tc_flow *flow;
-       struct mlx5e_tc_table *tc = &priv->fs.tc;
 
-       flow = rhashtable_lookup_fast(&tc->ht, &f->cookie,
-                                     tc->ht_params);
-       if (!flow)
+       flow = rhashtable_lookup_fast(tc_ht, &f->cookie, tc_ht_params);
+       if (!flow || !same_flow_direction(flow, flags))
                return -EINVAL;
 
-       rhashtable_remove_fast(&tc->ht, &flow->node, tc->ht_params);
+       rhashtable_remove_fast(tc_ht, &flow->node, tc_ht_params);
 
        mlx5e_tc_del_flow(priv, flow);
 
@@ -2659,18 +2765,17 @@ int mlx5e_delete_flower(struct mlx5e_priv *priv,
 }
 
 int mlx5e_stats_flower(struct mlx5e_priv *priv,
-                      struct tc_cls_flower_offload *f)
+                      struct tc_cls_flower_offload *f, int flags)
 {
-       struct mlx5e_tc_table *tc = &priv->fs.tc;
+       struct rhashtable *tc_ht = get_tc_ht(priv);
        struct mlx5e_tc_flow *flow;
        struct mlx5_fc *counter;
        u64 bytes;
        u64 packets;
        u64 lastuse;
 
-       flow = rhashtable_lookup_fast(&tc->ht, &f->cookie,
-                                     tc->ht_params);
-       if (!flow)
+       flow = rhashtable_lookup_fast(tc_ht, &f->cookie, tc_ht_params);
+       if (!flow || !same_flow_direction(flow, flags))
                return -EINVAL;
 
        if (!(flow->flags & MLX5E_TC_FLOW_OFFLOADED))
@@ -2687,41 +2792,43 @@ int mlx5e_stats_flower(struct mlx5e_priv *priv,
        return 0;
 }
 
-static const struct rhashtable_params mlx5e_tc_flow_ht_params = {
-       .head_offset = offsetof(struct mlx5e_tc_flow, node),
-       .key_offset = offsetof(struct mlx5e_tc_flow, cookie),
-       .key_len = sizeof(((struct mlx5e_tc_flow *)0)->cookie),
-       .automatic_shrinking = true,
-};
-
-int mlx5e_tc_init(struct mlx5e_priv *priv)
+int mlx5e_tc_nic_init(struct mlx5e_priv *priv)
 {
        struct mlx5e_tc_table *tc = &priv->fs.tc;
 
        hash_init(tc->mod_hdr_tbl);
        hash_init(tc->hairpin_tbl);
 
-       tc->ht_params = mlx5e_tc_flow_ht_params;
-       return rhashtable_init(&tc->ht, &tc->ht_params);
+       return rhashtable_init(&tc->ht, &tc_ht_params);
 }
 
 static void _mlx5e_tc_del_flow(void *ptr, void *arg)
 {
        struct mlx5e_tc_flow *flow = ptr;
-       struct mlx5e_priv *priv = arg;
+       struct mlx5e_priv *priv = flow->priv;
 
        mlx5e_tc_del_flow(priv, flow);
        kfree(flow);
 }
 
-void mlx5e_tc_cleanup(struct mlx5e_priv *priv)
+void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv)
 {
        struct mlx5e_tc_table *tc = &priv->fs.tc;
 
-       rhashtable_free_and_destroy(&tc->ht, _mlx5e_tc_del_flow, priv);
+       rhashtable_free_and_destroy(&tc->ht, _mlx5e_tc_del_flow, NULL);
 
        if (!IS_ERR_OR_NULL(tc->t)) {
                mlx5_destroy_flow_table(tc->t);
                tc->t = NULL;
        }
 }
+
+int mlx5e_tc_esw_init(struct rhashtable *tc_ht)
+{
+       return rhashtable_init(tc_ht, &tc_ht_params);
+}
+
+void mlx5e_tc_esw_cleanup(struct rhashtable *tc_ht)
+{
+       rhashtable_free_and_destroy(tc_ht, _mlx5e_tc_del_flow, NULL);
+}
index c14c263..59e52b8 100644 (file)
 #define MLX5E_TC_FLOW_ID_MASK 0x0000ffff
 
 #ifdef CONFIG_MLX5_ESWITCH
-int mlx5e_tc_init(struct mlx5e_priv *priv);
-void mlx5e_tc_cleanup(struct mlx5e_priv *priv);
+
+enum {
+       MLX5E_TC_INGRESS = BIT(0),
+       MLX5E_TC_EGRESS  = BIT(1),
+       MLX5E_TC_LAST_EXPORTED_BIT = 1,
+};
+
+int mlx5e_tc_nic_init(struct mlx5e_priv *priv);
+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,
-                          struct tc_cls_flower_offload *f);
+                          struct tc_cls_flower_offload *f, int flags);
 int mlx5e_delete_flower(struct mlx5e_priv *priv,
-                       struct tc_cls_flower_offload *f);
+                       struct tc_cls_flower_offload *f, int flags);
 
 int mlx5e_stats_flower(struct mlx5e_priv *priv,
-                      struct tc_cls_flower_offload *f);
+                      struct tc_cls_flower_offload *f, int flags);
 
 struct mlx5e_encap_entry;
 void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv,
@@ -64,8 +74,8 @@ static inline int mlx5e_tc_num_filters(struct mlx5e_priv *priv)
 }
 
 #else /* CONFIG_MLX5_ESWITCH */
-static inline int  mlx5e_tc_init(struct mlx5e_priv *priv) { return 0; }
-static inline void mlx5e_tc_cleanup(struct mlx5e_priv *priv) {}
+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; }
 #endif
 
index 047614d..2d3f17d 100644 (file)
@@ -450,7 +450,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
 
        sq = container_of(cq, struct mlx5e_txqsq, cq);
 
-       if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED)))
+       if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state)))
                return false;
 
        cqe = mlx5_cqwq_get_cqe(&cq->wq);
index 900661d..5d6f9ce 100644 (file)
@@ -48,7 +48,7 @@ static void mlx5e_handle_tx_dim(struct mlx5e_txqsq *sq)
 {
        struct net_dim_sample dim_sample;
 
-       if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_AM)))
+       if (unlikely(!test_bit(MLX5E_SQ_STATE_AM, &sq->state)))
                return;
 
        net_dim_sample(sq->cq.event_ctr, sq->stats.packets, sq->stats.bytes,
@@ -60,7 +60,7 @@ static void mlx5e_handle_rx_dim(struct mlx5e_rq *rq)
 {
        struct net_dim_sample dim_sample;
 
-       if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_AM)))
+       if (unlikely(!test_bit(MLX5E_RQ_STATE_AM, &rq->state)))
                return;
 
        net_dim_sample(rq->cq.event_ctr, rq->stats.packets, rq->stats.bytes,
index 1352d13..09f0e11 100644 (file)
@@ -192,7 +192,7 @@ __esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule,
        }
 
        dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
-       dest.vport_num = vport;
+       dest.vport.num = vport;
 
        esw_debug(esw->dev,
                  "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n",
index 4cd773f..f47a14e 100644 (file)
@@ -227,9 +227,18 @@ enum {
        SET_VLAN_INSERT = BIT(1)
 };
 
+enum mlx5_flow_match_level {
+       MLX5_MATCH_NONE = MLX5_INLINE_MODE_NONE,
+       MLX5_MATCH_L2   = MLX5_INLINE_MODE_L2,
+       MLX5_MATCH_L3   = MLX5_INLINE_MODE_IP,
+       MLX5_MATCH_L4   = MLX5_INLINE_MODE_TCP_UDP,
+};
+
 struct mlx5_esw_flow_attr {
        struct mlx5_eswitch_rep *in_rep;
        struct mlx5_eswitch_rep *out_rep;
+       struct mlx5_core_dev    *out_mdev;
+       struct mlx5_core_dev    *in_mdev;
 
        int     action;
        __be16  vlan_proto;
@@ -238,6 +247,7 @@ struct mlx5_esw_flow_attr {
        bool    vlan_handled;
        u32     encap_id;
        u32     mod_hdr_id;
+       u8      match_level;
        struct mlx5e_tc_flow_parse_attr *parse_attr;
 };
 
index 35e256e..b9ea464 100644 (file)
@@ -71,7 +71,12 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
 
        if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
                dest[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
-               dest[i].vport_num = attr->out_rep->vport;
+               dest[i].vport.num = attr->out_rep->vport;
+               if (MLX5_CAP_ESW(esw->dev, merged_eswitch)) {
+                       dest[i].vport.vhca_id =
+                               MLX5_CAP_GEN(attr->out_mdev, vhca_id);
+                       dest[i].vport.vhca_id_valid = 1;
+               }
                i++;
        }
        if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
@@ -88,11 +93,23 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
        misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
        MLX5_SET(fte_match_set_misc, misc, source_port, attr->in_rep->vport);
 
+       if (MLX5_CAP_ESW(esw->dev, merged_eswitch))
+               MLX5_SET(fte_match_set_misc, misc,
+                        source_eswitch_owner_vhca_id,
+                        MLX5_CAP_GEN(attr->in_mdev, vhca_id));
+
        misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
        MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
+       if (MLX5_CAP_ESW(esw->dev, merged_eswitch))
+               MLX5_SET_TO_ONES(fte_match_set_misc, misc,
+                                source_eswitch_owner_vhca_id);
+
+       if (attr->match_level == MLX5_MATCH_NONE)
+               spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
+       else
+               spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS |
+                                             MLX5_MATCH_MISC_PARAMETERS;
 
-       spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS |
-                                     MLX5_MATCH_MISC_PARAMETERS;
        if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DECAP)
                spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS;
 
@@ -343,7 +360,7 @@ mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport, u32 sqn
 
        spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
        dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
-       dest.vport_num = vport;
+       dest.vport.num = vport;
        flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
 
        flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec,
@@ -387,7 +404,7 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
        dmac_c[0] = 0x01;
 
        dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
-       dest.vport_num = 0;
+       dest.vport.num = 0;
        flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
 
        flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec,
@@ -663,7 +680,7 @@ static int esw_create_vport_rx_group(struct mlx5_eswitch *esw)
 
        esw->offloads.vport_rx_group = g;
 out:
-       kfree(flow_group_in);
+       kvfree(flow_group_in);
        return err;
 }
 
index ef5afd7..5a00def 100644 (file)
@@ -372,6 +372,15 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
                        if (dst->dest_attr.type ==
                            MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE) {
                                id = dst->dest_attr.ft->id;
+                       } else if (dst->dest_attr.type ==
+                                  MLX5_FLOW_DESTINATION_TYPE_VPORT) {
+                               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);
+                               MLX5_SET(dest_format_struct, in_dests,
+                                        destination_eswitch_owner_vhca_id,
+                                        dst->dest_attr.vport.vhca_id);
                        } else {
                                id = dst->dest_attr.tir_num;
                        }
index c39c169..806e955 100644 (file)
@@ -1374,6 +1374,8 @@ static int create_auto_flow_group(struct mlx5_flow_table *ft,
        struct mlx5_core_dev *dev = get_dev(&ft->node);
        int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
        void *match_criteria_addr;
+       u8 src_esw_owner_mask_on;
+       void *misc;
        int err;
        u32 *in;
 
@@ -1386,6 +1388,14 @@ static int create_auto_flow_group(struct mlx5_flow_table *ft,
        MLX5_SET(create_flow_group_in, in, start_flow_index, fg->start_index);
        MLX5_SET(create_flow_group_in, in, end_flow_index,   fg->start_index +
                 fg->max_ftes - 1);
+
+       misc = MLX5_ADDR_OF(fte_match_param, fg->mask.match_criteria,
+                           misc_parameters);
+       src_esw_owner_mask_on = !!MLX5_GET(fte_match_set_misc, misc,
+                                        source_eswitch_owner_vhca_id);
+       MLX5_SET(create_flow_group_in, in,
+                source_eswitch_owner_vhca_id_valid, src_esw_owner_mask_on);
+
        match_criteria_addr = MLX5_ADDR_OF(create_flow_group_in,
                                           in, match_criteria);
        memcpy(match_criteria_addr, fg->mask.match_criteria,
@@ -1406,7 +1416,7 @@ 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->type == MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE &&
                     d1->ft == d2->ft) ||
                    (d1->type == MLX5_FLOW_DESTINATION_TYPE_TIR &&
index 177e076..719cecb 100644 (file)
@@ -511,7 +511,7 @@ int mlx5_query_nic_vport_system_image_guid(struct mlx5_core_dev *mdev,
        *system_image_guid = MLX5_GET64(query_nic_vport_context_out, out,
                                        nic_vport_context.system_image_guid);
 
-       kfree(out);
+       kvfree(out);
 
        return 0;
 }
@@ -531,7 +531,7 @@ int mlx5_query_nic_vport_node_guid(struct mlx5_core_dev *mdev, u64 *node_guid)
        *node_guid = MLX5_GET64(query_nic_vport_context_out, out,
                                nic_vport_context.node_guid);
 
-       kfree(out);
+       kvfree(out);
 
        return 0;
 }
@@ -587,7 +587,7 @@ int mlx5_query_nic_vport_qkey_viol_cntr(struct mlx5_core_dev *mdev,
        *qkey_viol_cntr = MLX5_GET(query_nic_vport_context_out, out,
                                   nic_vport_context.qkey_violation_counter);
 
-       kfree(out);
+       kvfree(out);
 
        return 0;
 }
index e13ac3b..a38faec 100644 (file)
@@ -1714,15 +1714,16 @@ EXPORT_SYMBOL(mlxsw_core_port_fini);
 
 void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u8 local_port,
                             void *port_driver_priv, struct net_device *dev,
-                            bool split, u32 split_group)
+                            u32 port_number, bool split,
+                            u32 split_port_subnumber)
 {
        struct mlxsw_core_port *mlxsw_core_port =
                                        &mlxsw_core->ports[local_port];
        struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
 
        mlxsw_core_port->port_driver_priv = port_driver_priv;
-       if (split)
-               devlink_port_split_set(devlink_port, split_group);
+       devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
+                              port_number, split, split_port_subnumber);
        devlink_port_type_eth_set(devlink_port, dev);
 }
 EXPORT_SYMBOL(mlxsw_core_port_eth_set);
@@ -1762,6 +1763,17 @@ enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core,
 }
 EXPORT_SYMBOL(mlxsw_core_port_type_get);
 
+int mlxsw_core_port_get_phys_port_name(struct mlxsw_core *mlxsw_core,
+                                      u8 local_port, char *name, size_t len)
+{
+       struct mlxsw_core_port *mlxsw_core_port =
+                                       &mlxsw_core->ports[local_port];
+       struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
+
+       return devlink_port_get_phys_port_name(devlink_port, name, len);
+}
+EXPORT_SYMBOL(mlxsw_core_port_get_phys_port_name);
+
 static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core,
                                    const char *buf, size_t size)
 {
index 092d393..4eac7fb 100644 (file)
@@ -201,13 +201,16 @@ int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port);
 void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port);
 void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u8 local_port,
                             void *port_driver_priv, struct net_device *dev,
-                            bool split, u32 split_group);
+                            u32 port_number, bool split,
+                            u32 split_port_subnumber);
 void mlxsw_core_port_ib_set(struct mlxsw_core *mlxsw_core, u8 local_port,
                            void *port_driver_priv);
 void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u8 local_port,
                           void *port_driver_priv);
 enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core,
                                                u8 local_port);
+int mlxsw_core_port_get_phys_port_name(struct mlxsw_core *mlxsw_core,
+                                      u8 local_port, char *name, size_t len);
 
 int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay);
 bool mlxsw_core_schedule_work(struct work_struct *work);
index 94132f6..bb252b3 100644 (file)
@@ -1238,21 +1238,10 @@ static int mlxsw_sp_port_get_phys_port_name(struct net_device *dev, char *name,
                                            size_t len)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
-       u8 module = mlxsw_sp_port->mapping.module;
-       u8 width = mlxsw_sp_port->mapping.width;
-       u8 lane = mlxsw_sp_port->mapping.lane;
-       int err;
-
-       if (!mlxsw_sp_port->split)
-               err = snprintf(name, len, "p%d", module + 1);
-       else
-               err = snprintf(name, len, "p%ds%d", module + 1,
-                              lane / width);
 
-       if (err >= len)
-               return -EINVAL;
-
-       return 0;
+       return mlxsw_core_port_get_phys_port_name(mlxsw_sp_port->mlxsw_sp->core,
+                                                 mlxsw_sp_port->local_port,
+                                                 name, len);
 }
 
 static struct mlxsw_sp_port_mall_tc_entry *
@@ -2927,8 +2916,8 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
        }
 
        mlxsw_core_port_eth_set(mlxsw_sp->core, mlxsw_sp_port->local_port,
-                               mlxsw_sp_port, dev, mlxsw_sp_port->split,
-                               module);
+                               mlxsw_sp_port, dev, module + 1,
+                               mlxsw_sp_port->split, lane / width);
        mlxsw_core_schedule_dw(&mlxsw_sp_port->periodic_hw_stats.update_dw, 0);
        return 0;
 
index 8028d22..77b2adb 100644 (file)
@@ -5725,6 +5725,7 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
 
        switch (fib_work->event) {
        case FIB_EVENT_ENTRY_REPLACE: /* fall through */
+       case FIB_EVENT_ENTRY_APPEND: /* fall through */
        case FIB_EVENT_ENTRY_ADD:
                replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
                err = mlxsw_sp_router_fib6_add(mlxsw_sp,
@@ -5831,6 +5832,7 @@ static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
 
        switch (fib_work->event) {
        case FIB_EVENT_ENTRY_REPLACE: /* fall through */
+       case FIB_EVENT_ENTRY_APPEND: /* fall through */
        case FIB_EVENT_ENTRY_ADD: /* fall through */
        case FIB_EVENT_ENTRY_DEL:
                fen6_info = container_of(info, struct fib6_entry_notifier_info,
index e5f4f76..da3f7f5 100644 (file)
@@ -245,6 +245,19 @@ mlxsw_sp_span_entry_vlan(const struct net_device *vlan_dev,
        return vlan_dev_real_dev(vlan_dev);
 }
 
+static struct net_device *
+mlxsw_sp_span_entry_lag(struct net_device *lag_dev)
+{
+       struct net_device *dev;
+       struct list_head *iter;
+
+       netdev_for_each_lower_dev(lag_dev, dev, iter)
+               if ((dev->flags & IFF_UP) && mlxsw_sp_port_dev_check(dev))
+                       return dev;
+
+       return NULL;
+}
+
 static __maybe_unused int
 mlxsw_sp_span_entry_tunnel_parms_common(struct net_device *edev,
                                        union mlxsw_sp_l3addr saddr,
@@ -278,6 +291,14 @@ mlxsw_sp_span_entry_tunnel_parms_common(struct net_device *edev,
                edev = mlxsw_sp_span_entry_vlan(edev, &vid);
        }
 
+       if (netif_is_lag_master(edev)) {
+               if (!(edev->flags & IFF_UP))
+                       goto unoffloadable;
+               edev = mlxsw_sp_span_entry_lag(edev);
+               if (!edev)
+                       goto unoffloadable;
+       }
+
        if (!mlxsw_sp_port_dev_check(edev))
                goto unoffloadable;
 
index a655c58..3922c1c 100644 (file)
@@ -417,13 +417,10 @@ static int mlxsw_sx_port_get_phys_port_name(struct net_device *dev, char *name,
                                            size_t len)
 {
        struct mlxsw_sx_port *mlxsw_sx_port = netdev_priv(dev);
-       int err;
-
-       err = snprintf(name, len, "p%d", mlxsw_sx_port->mapping.module + 1);
-       if (err >= len)
-               return -EINVAL;
 
-       return 0;
+       return mlxsw_core_port_get_phys_port_name(mlxsw_sx_port->mlxsw_sx->core,
+                                                 mlxsw_sx_port->local_port,
+                                                 name, len);
 }
 
 static const struct net_device_ops mlxsw_sx_port_netdev_ops = {
@@ -1149,7 +1146,7 @@ static int __mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port,
        }
 
        mlxsw_core_port_eth_set(mlxsw_sx->core, mlxsw_sx_port->local_port,
-                               mlxsw_sx_port, dev, false, 0);
+                               mlxsw_sx_port, dev, module + 1, false, 0);
        mlxsw_sx->ports[local_port] = mlxsw_sx_port;
        return 0;
 
diff --git a/drivers/net/ethernet/mscc/Kconfig b/drivers/net/ethernet/mscc/Kconfig
new file mode 100644 (file)
index 0000000..36c8462
--- /dev/null
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: (GPL-2.0 OR MIT)
+config NET_VENDOR_MICROSEMI
+       bool "Microsemi devices"
+       default y
+       help
+         If you have a network (Ethernet) card belonging to this class, say Y.
+
+         Note that the answer to this question doesn't directly affect the
+         kernel: saying N will just cause the configurator to skip all
+         the questions about Microsemi devices.
+
+if NET_VENDOR_MICROSEMI
+
+config MSCC_OCELOT_SWITCH
+       tristate "Ocelot switch driver"
+       depends on NET_SWITCHDEV
+       depends on HAS_IOMEM
+       select PHYLIB
+       select REGMAP_MMIO
+       help
+         This driver supports the Ocelot network switch device.
+
+config MSCC_OCELOT_SWITCH_OCELOT
+       tristate "Ocelot switch driver on Ocelot"
+       depends on MSCC_OCELOT_SWITCH
+       help
+         This driver supports the Ocelot network switch device as present on
+         the Ocelot SoCs.
+
+endif # NET_VENDOR_MICROSEMI
diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
new file mode 100644 (file)
index 0000000..cb52a3b
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: (GPL-2.0 OR MIT)
+obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o
+mscc_ocelot_common-y := ocelot.o ocelot_io.o
+mscc_ocelot_common-y += ocelot_regs.o
+obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
new file mode 100644 (file)
index 0000000..c8c74aa
--- /dev/null
@@ -0,0 +1,1333 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_bridge.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/skbuff.h>
+#include <net/arp.h>
+#include <net/netevent.h>
+#include <net/rtnetlink.h>
+#include <net/switchdev.h>
+
+#include "ocelot.h"
+
+/* MAC table entry types.
+ * ENTRYTYPE_NORMAL is subject to aging.
+ * ENTRYTYPE_LOCKED is not subject to aging.
+ * ENTRYTYPE_MACv4 is not subject to aging. For IPv4 multicast.
+ * ENTRYTYPE_MACv6 is not subject to aging. For IPv6 multicast.
+ */
+enum macaccess_entry_type {
+       ENTRYTYPE_NORMAL = 0,
+       ENTRYTYPE_LOCKED,
+       ENTRYTYPE_MACv4,
+       ENTRYTYPE_MACv6,
+};
+
+struct ocelot_mact_entry {
+       u8 mac[ETH_ALEN];
+       u16 vid;
+       enum macaccess_entry_type type;
+};
+
+static inline int ocelot_mact_wait_for_completion(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--);
+
+       if (!timeout)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static void ocelot_mact_select(struct ocelot *ocelot,
+                              const unsigned char mac[ETH_ALEN],
+                              unsigned int vid)
+{
+       u32 macl = 0, mach = 0;
+
+       /* Set the MAC address to handle and the vlan associated in a format
+        * understood by the hardware.
+        */
+       mach |= vid    << 16;
+       mach |= mac[0] << 8;
+       mach |= mac[1] << 0;
+       macl |= mac[2] << 24;
+       macl |= mac[3] << 16;
+       macl |= mac[4] << 8;
+       macl |= mac[5] << 0;
+
+       ocelot_write(ocelot, macl, ANA_TABLES_MACLDATA);
+       ocelot_write(ocelot, mach, ANA_TABLES_MACHDATA);
+
+}
+
+static int ocelot_mact_learn(struct ocelot *ocelot, int port,
+                            const unsigned char mac[ETH_ALEN],
+                            unsigned int vid,
+                            enum macaccess_entry_type type)
+{
+       ocelot_mact_select(ocelot, mac, vid);
+
+       /* Issue a write command */
+       ocelot_write(ocelot, ANA_TABLES_MACACCESS_VALID |
+                            ANA_TABLES_MACACCESS_DEST_IDX(port) |
+                            ANA_TABLES_MACACCESS_ENTRYTYPE(type) |
+                            ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_LEARN),
+                            ANA_TABLES_MACACCESS);
+
+       return ocelot_mact_wait_for_completion(ocelot);
+}
+
+static int ocelot_mact_forget(struct ocelot *ocelot,
+                             const unsigned char mac[ETH_ALEN],
+                             unsigned int vid)
+{
+       ocelot_mact_select(ocelot, mac, vid);
+
+       /* Issue a forget command */
+       ocelot_write(ocelot,
+                    ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_FORGET),
+                    ANA_TABLES_MACACCESS);
+
+       return ocelot_mact_wait_for_completion(ocelot);
+}
+
+static void ocelot_mact_init(struct ocelot *ocelot)
+{
+       /* Configure the learning mode entries attributes:
+        * - Do not copy the frame to the CPU extraction queues.
+        * - Use the vlan and mac_cpoy for dmac lookup.
+        */
+       ocelot_rmw(ocelot, 0,
+                  ANA_AGENCTRL_LEARN_CPU_COPY | ANA_AGENCTRL_IGNORE_DMAC_FLAGS
+                  | ANA_AGENCTRL_LEARN_FWD_KILL
+                  | ANA_AGENCTRL_LEARN_IGNORE_VLAN,
+                  ANA_AGENCTRL);
+
+       /* Clear the MAC table */
+       ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS);
+}
+
+static inline int ocelot_vlant_wait_for_completion(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_VLANACCESS);
+               val &= ANA_TABLES_VLANACCESS_VLAN_TBL_CMD_M;
+       } while (val != ANA_TABLES_VLANACCESS_CMD_IDLE && timeout--);
+
+       if (!timeout)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static void ocelot_vlan_init(struct ocelot *ocelot)
+{
+       /* Clear VLAN table, by default all ports are members of all VLANs */
+       ocelot_write(ocelot, ANA_TABLES_VLANACCESS_CMD_INIT,
+                    ANA_TABLES_VLANACCESS);
+       ocelot_vlant_wait_for_completion(ocelot);
+}
+
+/* Watermark encode
+ * Bit 8:   Unit; 0:1, 1:16
+ * Bit 7-0: Value to be multiplied with unit
+ */
+static u16 ocelot_wm_enc(u16 value)
+{
+       if (value >= BIT(8))
+               return BIT(8) | (value / 16);
+
+       return value;
+}
+
+static void ocelot_port_adjust_link(struct net_device *dev)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+       u8 p = port->chip_port;
+       int speed, atop_wm, mode = 0;
+
+       switch (dev->phydev->speed) {
+       case SPEED_10:
+               speed = OCELOT_SPEED_10;
+               break;
+       case SPEED_100:
+               speed = OCELOT_SPEED_100;
+               break;
+       case SPEED_1000:
+               speed = OCELOT_SPEED_1000;
+               mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
+               break;
+       case SPEED_2500:
+               speed = OCELOT_SPEED_2500;
+               mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
+               break;
+       default:
+               netdev_err(dev, "Unsupported PHY speed: %d\n",
+                          dev->phydev->speed);
+               return;
+       }
+
+       phy_print_status(dev->phydev);
+
+       if (!dev->phydev->link)
+               return;
+
+       /* Only full duplex supported for now */
+       ocelot_port_writel(port, DEV_MAC_MODE_CFG_FDX_ENA |
+                          mode, DEV_MAC_MODE_CFG);
+
+       /* Set MAC IFG Gaps
+        * FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 0
+        * !FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 5
+        */
+       ocelot_port_writel(port, DEV_MAC_IFG_CFG_TX_IFG(5), DEV_MAC_IFG_CFG);
+
+       /* Load seed (0) and set MAC HDX late collision  */
+       ocelot_port_writel(port, DEV_MAC_HDX_CFG_LATE_COL_POS(67) |
+                          DEV_MAC_HDX_CFG_SEED_LOAD,
+                          DEV_MAC_HDX_CFG);
+       mdelay(1);
+       ocelot_port_writel(port, DEV_MAC_HDX_CFG_LATE_COL_POS(67),
+                          DEV_MAC_HDX_CFG);
+
+       /* Disable HDX fast control */
+       ocelot_port_writel(port, DEV_PORT_MISC_HDX_FAST_DIS, DEV_PORT_MISC);
+
+       /* SGMII only for now */
+       ocelot_port_writel(port, PCS1G_MODE_CFG_SGMII_MODE_ENA, PCS1G_MODE_CFG);
+       ocelot_port_writel(port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG);
+
+       /* Enable PCS */
+       ocelot_port_writel(port, PCS1G_CFG_PCS_ENA, PCS1G_CFG);
+
+       /* No aneg on SGMII */
+       ocelot_port_writel(port, 0, PCS1G_ANEG_CFG);
+
+       /* No loopback */
+       ocelot_port_writel(port, 0, PCS1G_LB_CFG);
+
+       /* Set Max Length and maximum tags allowed */
+       ocelot_port_writel(port, VLAN_ETH_FRAME_LEN, DEV_MAC_MAXLEN_CFG);
+       ocelot_port_writel(port, DEV_MAC_TAGS_CFG_TAG_ID(ETH_P_8021AD) |
+                          DEV_MAC_TAGS_CFG_VLAN_AWR_ENA |
+                          DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA,
+                          DEV_MAC_TAGS_CFG);
+
+       /* Enable MAC module */
+       ocelot_port_writel(port, DEV_MAC_ENA_CFG_RX_ENA |
+                          DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);
+
+       /* Take MAC, Port, Phy (intern) and PCS (SGMII/Serdes) clock out of
+        * reset */
+       ocelot_port_writel(port, DEV_CLOCK_CFG_LINK_SPEED(speed),
+                          DEV_CLOCK_CFG);
+
+       /* Set SMAC of Pause frame (00:00:00:00:00:00) */
+       ocelot_port_writel(port, 0, DEV_MAC_FC_MAC_HIGH_CFG);
+       ocelot_port_writel(port, 0, DEV_MAC_FC_MAC_LOW_CFG);
+
+       /* No PFC */
+       ocelot_write_gix(ocelot, ANA_PFC_PFC_CFG_FC_LINK_SPEED(speed),
+                        ANA_PFC_PFC_CFG, p);
+
+       /* Set Pause WM hysteresis
+        * 152 = 6 * VLAN_ETH_FRAME_LEN / OCELOT_BUFFER_CELL_SZ
+        * 101 = 4 * VLAN_ETH_FRAME_LEN / OCELOT_BUFFER_CELL_SZ
+        */
+       ocelot_write_rix(ocelot, SYS_PAUSE_CFG_PAUSE_ENA |
+                        SYS_PAUSE_CFG_PAUSE_STOP(101) |
+                        SYS_PAUSE_CFG_PAUSE_START(152), SYS_PAUSE_CFG, p);
+
+       /* Core: Enable port for frame transfer */
+       ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE |
+                        QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) |
+                        QSYS_SWITCH_PORT_MODE_PORT_ENA,
+                        QSYS_SWITCH_PORT_MODE, p);
+
+       /* Flow control */
+       ocelot_write_rix(ocelot, SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
+                        SYS_MAC_FC_CFG_RX_FC_ENA | SYS_MAC_FC_CFG_TX_FC_ENA |
+                        SYS_MAC_FC_CFG_ZERO_PAUSE_ENA |
+                        SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
+                        SYS_MAC_FC_CFG_FC_LINK_SPEED(speed),
+                        SYS_MAC_FC_CFG, p);
+       ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, p);
+
+       /* Tail dropping watermark */
+       atop_wm = (ocelot->shared_queue_sz - 9 * VLAN_ETH_FRAME_LEN) / OCELOT_BUFFER_CELL_SZ;
+       ocelot_write_rix(ocelot, ocelot_wm_enc(9 * VLAN_ETH_FRAME_LEN),
+                        SYS_ATOP, p);
+       ocelot_write(ocelot, ocelot_wm_enc(atop_wm), SYS_ATOP_TOT_CFG);
+}
+
+static int ocelot_port_open(struct net_device *dev)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+       int err;
+
+       /* Enable receiving frames on the port, and activate auto-learning of
+        * MAC addresses.
+        */
+       ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO |
+                        ANA_PORT_PORT_CFG_RECV_ENA |
+                        ANA_PORT_PORT_CFG_PORTID_VAL(port->chip_port),
+                        ANA_PORT_PORT_CFG, port->chip_port);
+
+       err = phy_connect_direct(dev, port->phy, &ocelot_port_adjust_link,
+                                PHY_INTERFACE_MODE_NA);
+       if (err) {
+               netdev_err(dev, "Could not attach to PHY\n");
+               return err;
+       }
+
+       dev->phydev = port->phy;
+
+       phy_attached_info(port->phy);
+       phy_start(port->phy);
+       return 0;
+}
+
+static int ocelot_port_stop(struct net_device *dev)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+
+       phy_disconnect(port->phy);
+
+       dev->phydev = NULL;
+
+       ocelot_port_writel(port, 0, DEV_MAC_ENA_CFG);
+       ocelot_rmw_rix(port->ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA,
+                        QSYS_SWITCH_PORT_MODE, port->chip_port);
+       return 0;
+}
+
+/* Generate the IFH for frame injection
+ *
+ * The IFH is a 128bit-value
+ * bit 127: bypass the analyzer processing
+ * bit 56-67: destination mask
+ * bit 28-29: pop_cnt: 3 disables all rewriting of the frame
+ * bit 20-27: cpu extraction queue mask
+ * bit 16: tag type 0: C-tag, 1: S-tag
+ * bit 0-11: VID
+ */
+static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info)
+{
+       ifh[0] = IFH_INJ_BYPASS;
+       ifh[1] = (0xff00 & info->port) >> 8;
+       ifh[2] = (0xff & info->port) << 24;
+       ifh[3] = IFH_INJ_POP_CNT_DISABLE | (info->cpuq << 20) |
+                (info->tag_type << 16) | info->vid;
+
+       return 0;
+}
+
+static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+       u32 val, ifh[IFH_LEN];
+       struct frame_info info = {};
+       u8 grp = 0; /* Send everything on CPU group 0 */
+       unsigned int i, count, last;
+
+       val = ocelot_read(ocelot, QS_INJ_STATUS);
+       if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
+           (val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp))))
+               return NETDEV_TX_BUSY;
+
+       ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
+                        QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp);
+
+       info.port = BIT(port->chip_port);
+       info.cpuq = 0xff;
+       ocelot_gen_ifh(ifh, &info);
+
+       for (i = 0; i < IFH_LEN; i++)
+               ocelot_write_rix(ocelot, ifh[i], QS_INJ_WR, grp);
+
+       count = (skb->len + 3) / 4;
+       last = skb->len % 4;
+       for (i = 0; i < count; i++) {
+               ocelot_write_rix(ocelot, ((u32 *)skb->data)[i], QS_INJ_WR, grp);
+       }
+
+       /* Add padding */
+       while (i < (OCELOT_BUFFER_CELL_SZ / 4)) {
+               ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp);
+               i++;
+       }
+
+       /* Indicate EOF and valid bytes in last word */
+       ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
+                        QS_INJ_CTRL_VLD_BYTES(skb->len < OCELOT_BUFFER_CELL_SZ ? 0 : last) |
+                        QS_INJ_CTRL_EOF,
+                        QS_INJ_CTRL, grp);
+
+       /* Add dummy CRC */
+       ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp);
+       skb_tx_timestamp(skb);
+
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += skb->len;
+       dev_kfree_skb_any(skb);
+
+       return NETDEV_TX_OK;
+}
+
+static void ocelot_mact_mc_reset(struct ocelot_port *port)
+{
+       struct ocelot *ocelot = port->ocelot;
+       struct netdev_hw_addr *ha, *n;
+
+       /* Free and forget all the MAC addresses stored in the port private mc
+        * list. These are mc addresses that were previously added by calling
+        * ocelot_mact_mc_add().
+        */
+       list_for_each_entry_safe(ha, n, &port->mc, list) {
+               ocelot_mact_forget(ocelot, ha->addr, port->pvid);
+               list_del(&ha->list);
+               kfree(ha);
+       }
+}
+
+static int ocelot_mact_mc_add(struct ocelot_port *port,
+                             struct netdev_hw_addr *hw_addr)
+{
+       struct ocelot *ocelot = port->ocelot;
+       struct netdev_hw_addr *ha = kzalloc(sizeof(*ha), GFP_KERNEL);
+
+       if (!ha)
+               return -ENOMEM;
+
+       memcpy(ha, hw_addr, sizeof(*ha));
+       list_add_tail(&ha->list, &port->mc);
+
+       ocelot_mact_learn(ocelot, PGID_CPU, ha->addr, port->pvid,
+                         ENTRYTYPE_LOCKED);
+
+       return 0;
+}
+
+static void ocelot_set_rx_mode(struct net_device *dev)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+       struct netdev_hw_addr *ha;
+       int i;
+       u32 val;
+
+       /* This doesn't handle promiscuous mode because the bridge core is
+        * setting IFF_PROMISC on all slave interfaces and all frames would be
+        * forwarded to the CPU port.
+        */
+       val = GENMASK(ocelot->num_phys_ports - 1, 0);
+       for (i = ocelot->num_phys_ports + 1; i < PGID_CPU; i++)
+               ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i);
+
+       /* Handle the device multicast addresses. First remove all the
+        * previously installed addresses and then add the latest ones to the
+        * mac table.
+        */
+       ocelot_mact_mc_reset(port);
+       netdev_for_each_mc_addr(ha, dev)
+               ocelot_mact_mc_add(port, ha);
+}
+
+static int ocelot_port_get_phys_port_name(struct net_device *dev,
+                                         char *buf, size_t len)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       int ret;
+
+       ret = snprintf(buf, len, "p%d", port->chip_port);
+       if (ret >= len)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int ocelot_port_set_mac_address(struct net_device *dev, void *p)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+       const struct sockaddr *addr = p;
+
+       /* Learn the new net device MAC address in the mac table. */
+       ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, port->pvid,
+                         ENTRYTYPE_LOCKED);
+       /* Then forget the previous one. */
+       ocelot_mact_forget(ocelot, dev->dev_addr, port->pvid);
+
+       ether_addr_copy(dev->dev_addr, addr->sa_data);
+       return 0;
+}
+
+static void ocelot_get_stats64(struct net_device *dev,
+                              struct rtnl_link_stats64 *stats)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+
+       /* Configure the port to read the stats from */
+       ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port->chip_port),
+                    SYS_STAT_CFG);
+
+       /* Get Rx stats */
+       stats->rx_bytes = ocelot_read(ocelot, SYS_COUNT_RX_OCTETS);
+       stats->rx_packets = ocelot_read(ocelot, SYS_COUNT_RX_SHORTS) +
+                           ocelot_read(ocelot, SYS_COUNT_RX_FRAGMENTS) +
+                           ocelot_read(ocelot, SYS_COUNT_RX_JABBERS) +
+                           ocelot_read(ocelot, SYS_COUNT_RX_LONGS) +
+                           ocelot_read(ocelot, SYS_COUNT_RX_64) +
+                           ocelot_read(ocelot, SYS_COUNT_RX_65_127) +
+                           ocelot_read(ocelot, SYS_COUNT_RX_128_255) +
+                           ocelot_read(ocelot, SYS_COUNT_RX_256_1023) +
+                           ocelot_read(ocelot, SYS_COUNT_RX_1024_1526) +
+                           ocelot_read(ocelot, SYS_COUNT_RX_1527_MAX);
+       stats->multicast = ocelot_read(ocelot, SYS_COUNT_RX_MULTICAST);
+       stats->rx_dropped = dev->stats.rx_dropped;
+
+       /* Get Tx stats */
+       stats->tx_bytes = ocelot_read(ocelot, SYS_COUNT_TX_OCTETS);
+       stats->tx_packets = ocelot_read(ocelot, SYS_COUNT_TX_64) +
+                           ocelot_read(ocelot, SYS_COUNT_TX_65_127) +
+                           ocelot_read(ocelot, SYS_COUNT_TX_128_511) +
+                           ocelot_read(ocelot, SYS_COUNT_TX_512_1023) +
+                           ocelot_read(ocelot, SYS_COUNT_TX_1024_1526) +
+                           ocelot_read(ocelot, SYS_COUNT_TX_1527_MAX);
+       stats->tx_dropped = ocelot_read(ocelot, SYS_COUNT_TX_DROPS) +
+                           ocelot_read(ocelot, SYS_COUNT_TX_AGING);
+       stats->collisions = ocelot_read(ocelot, SYS_COUNT_TX_COLLISION);
+}
+
+static int ocelot_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
+                         struct net_device *dev, const unsigned char *addr,
+                         u16 vid, u16 flags)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+
+       return ocelot_mact_learn(ocelot, port->chip_port, addr, vid,
+                                ENTRYTYPE_NORMAL);
+}
+
+static int ocelot_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
+                         struct net_device *dev,
+                         const unsigned char *addr, u16 vid)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+
+       return ocelot_mact_forget(ocelot, addr, vid);
+}
+
+struct ocelot_dump_ctx {
+       struct net_device *dev;
+       struct sk_buff *skb;
+       struct netlink_callback *cb;
+       int idx;
+};
+
+static int ocelot_fdb_do_dump(struct ocelot_mact_entry *entry,
+                             struct ocelot_dump_ctx *dump)
+{
+       u32 portid = NETLINK_CB(dump->cb->skb).portid;
+       u32 seq = dump->cb->nlh->nlmsg_seq;
+       struct nlmsghdr *nlh;
+       struct ndmsg *ndm;
+
+       if (dump->idx < dump->cb->args[2])
+               goto skip;
+
+       nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
+                       sizeof(*ndm), NLM_F_MULTI);
+       if (!nlh)
+               return -EMSGSIZE;
+
+       ndm = nlmsg_data(nlh);
+       ndm->ndm_family  = AF_BRIDGE;
+       ndm->ndm_pad1    = 0;
+       ndm->ndm_pad2    = 0;
+       ndm->ndm_flags   = NTF_SELF;
+       ndm->ndm_type    = 0;
+       ndm->ndm_ifindex = dump->dev->ifindex;
+       ndm->ndm_state   = NUD_REACHABLE;
+
+       if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, entry->mac))
+               goto nla_put_failure;
+
+       if (entry->vid && nla_put_u16(dump->skb, NDA_VLAN, entry->vid))
+               goto nla_put_failure;
+
+       nlmsg_end(dump->skb, nlh);
+
+skip:
+       dump->idx++;
+       return 0;
+
+nla_put_failure:
+       nlmsg_cancel(dump->skb, nlh);
+       return -EMSGSIZE;
+}
+
+static inline int ocelot_mact_read(struct ocelot_port *port, int row, int col,
+                                  struct ocelot_mact_entry *entry)
+{
+       struct ocelot *ocelot = port->ocelot;
+       char mac[ETH_ALEN];
+       u32 val, dst, macl, mach;
+
+       /* Set row and column to read from */
+       ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_M_INDEX, row);
+       ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_BUCKET, col);
+
+       /* Issue a read command */
+       ocelot_write(ocelot,
+                    ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_READ),
+                    ANA_TABLES_MACACCESS);
+
+       if (ocelot_mact_wait_for_completion(ocelot))
+               return -ETIMEDOUT;
+
+       /* Read the entry flags */
+       val = ocelot_read(ocelot, ANA_TABLES_MACACCESS);
+       if (!(val & ANA_TABLES_MACACCESS_VALID))
+               return -EINVAL;
+
+       /* If the entry read has another port configured as its destination,
+        * do not report it.
+        */
+       dst = (val & ANA_TABLES_MACACCESS_DEST_IDX_M) >> 3;
+       if (dst != port->chip_port)
+               return -EINVAL;
+
+       /* Get the entry's MAC address and VLAN id */
+       macl = ocelot_read(ocelot, ANA_TABLES_MACLDATA);
+       mach = ocelot_read(ocelot, ANA_TABLES_MACHDATA);
+
+       mac[0] = (mach >> 8)  & 0xff;
+       mac[1] = (mach >> 0)  & 0xff;
+       mac[2] = (macl >> 24) & 0xff;
+       mac[3] = (macl >> 16) & 0xff;
+       mac[4] = (macl >> 8)  & 0xff;
+       mac[5] = (macl >> 0)  & 0xff;
+
+       entry->vid = (mach >> 16) & 0xfff;
+       ether_addr_copy(entry->mac, mac);
+
+       return 0;
+}
+
+static int ocelot_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
+                          struct net_device *dev,
+                          struct net_device *filter_dev, int *idx)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       int i, j, ret = 0;
+       struct ocelot_dump_ctx dump = {
+               .dev = dev,
+               .skb = skb,
+               .cb = cb,
+               .idx = *idx,
+       };
+
+       struct ocelot_mact_entry entry;
+
+       /* Loop through all the mac tables entries. There are 1024 rows of 4
+        * entries.
+        */
+       for (i = 0; i < 1024; i++) {
+               for (j = 0; j < 4; j++) {
+                       ret = ocelot_mact_read(port, i, j, &entry);
+                       /* If the entry is invalid (wrong port, invalid...),
+                        * skip it.
+                        */
+                       if (ret == -EINVAL)
+                               continue;
+                       else if (ret)
+                               goto end;
+
+                       ret = ocelot_fdb_do_dump(&entry, &dump);
+                       if (ret)
+                               goto end;
+               }
+       }
+
+end:
+       *idx = dump.idx;
+       return ret;
+}
+
+static const struct net_device_ops ocelot_port_netdev_ops = {
+       .ndo_open                       = ocelot_port_open,
+       .ndo_stop                       = ocelot_port_stop,
+       .ndo_start_xmit                 = ocelot_port_xmit,
+       .ndo_set_rx_mode                = ocelot_set_rx_mode,
+       .ndo_get_phys_port_name         = ocelot_port_get_phys_port_name,
+       .ndo_set_mac_address            = ocelot_port_set_mac_address,
+       .ndo_get_stats64                = ocelot_get_stats64,
+       .ndo_fdb_add                    = ocelot_fdb_add,
+       .ndo_fdb_del                    = ocelot_fdb_del,
+       .ndo_fdb_dump                   = ocelot_fdb_dump,
+};
+
+static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
+{
+       struct ocelot_port *port = netdev_priv(netdev);
+       struct ocelot *ocelot = port->ocelot;
+       int i;
+
+       if (sset != ETH_SS_STATS)
+               return;
+
+       for (i = 0; i < ocelot->num_stats; i++)
+               memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name,
+                      ETH_GSTRING_LEN);
+}
+
+static void ocelot_check_stats(struct work_struct *work)
+{
+       struct delayed_work *del_work = to_delayed_work(work);
+       struct ocelot *ocelot = container_of(del_work, struct ocelot, stats_work);
+       int i, j;
+
+       mutex_lock(&ocelot->stats_lock);
+
+       for (i = 0; i < ocelot->num_phys_ports; i++) {
+               /* Configure the port to read the stats from */
+               ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(i), SYS_STAT_CFG);
+
+               for (j = 0; j < ocelot->num_stats; j++) {
+                       u32 val;
+                       unsigned int idx = i * ocelot->num_stats + j;
+
+                       val = ocelot_read_rix(ocelot, SYS_COUNT_RX_OCTETS,
+                                             ocelot->stats_layout[j].offset);
+
+                       if (val < (ocelot->stats[idx] & U32_MAX))
+                               ocelot->stats[idx] += (u64)1 << 32;
+
+                       ocelot->stats[idx] = (ocelot->stats[idx] &
+                                             ~(u64)U32_MAX) + val;
+               }
+       }
+
+       cancel_delayed_work(&ocelot->stats_work);
+       queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
+                          OCELOT_STATS_CHECK_DELAY);
+
+       mutex_unlock(&ocelot->stats_lock);
+}
+
+static void ocelot_get_ethtool_stats(struct net_device *dev,
+                                    struct ethtool_stats *stats, u64 *data)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+       int i;
+
+       /* check and update now */
+       ocelot_check_stats(&ocelot->stats_work.work);
+
+       /* Copy all counters */
+       for (i = 0; i < ocelot->num_stats; i++)
+               *data++ = ocelot->stats[port->chip_port * ocelot->num_stats + i];
+}
+
+static int ocelot_get_sset_count(struct net_device *dev, int sset)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+
+       if (sset != ETH_SS_STATS)
+               return -EOPNOTSUPP;
+       return ocelot->num_stats;
+}
+
+static const struct ethtool_ops ocelot_ethtool_ops = {
+       .get_strings            = ocelot_get_strings,
+       .get_ethtool_stats      = ocelot_get_ethtool_stats,
+       .get_sset_count         = ocelot_get_sset_count,
+};
+
+static int ocelot_port_attr_get(struct net_device *dev,
+                               struct switchdev_attr *attr)
+{
+       struct ocelot_port *ocelot_port = netdev_priv(dev);
+       struct ocelot *ocelot = ocelot_port->ocelot;
+
+       switch (attr->id) {
+       case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
+               attr->u.ppid.id_len = sizeof(ocelot->base_mac);
+               memcpy(&attr->u.ppid.id, &ocelot->base_mac,
+                      attr->u.ppid.id_len);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port,
+                                         struct switchdev_trans *trans,
+                                         u8 state)
+{
+       struct ocelot *ocelot = ocelot_port->ocelot;
+       u32 port_cfg;
+       int port, i;
+
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
+       if (!(BIT(ocelot_port->chip_port) & ocelot->bridge_mask))
+               return 0;
+
+       port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG,
+                                  ocelot_port->chip_port);
+
+       switch (state) {
+       case BR_STATE_FORWARDING:
+               ocelot->bridge_fwd_mask |= BIT(ocelot_port->chip_port);
+               /* Fallthrough */
+       case BR_STATE_LEARNING:
+               port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA;
+               break;
+
+       default:
+               port_cfg &= ~ANA_PORT_PORT_CFG_LEARN_ENA;
+               ocelot->bridge_fwd_mask &= ~BIT(ocelot_port->chip_port);
+               break;
+       }
+
+       ocelot_write_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG,
+                        ocelot_port->chip_port);
+
+       /* Apply FWD mask. The loop is needed to add/remove the current port as
+        * a source for the other ports.
+        */
+       for (port = 0; port < ocelot->num_phys_ports; port++) {
+               if (ocelot->bridge_fwd_mask & BIT(port)) {
+                       unsigned long mask = ocelot->bridge_fwd_mask & ~BIT(port);
+
+                       for (i = 0; i < ocelot->num_phys_ports; i++) {
+                               unsigned long bond_mask = ocelot->lags[i];
+
+                               if (!bond_mask)
+                                       continue;
+
+                               if (bond_mask & BIT(port)) {
+                                       mask &= ~bond_mask;
+                                       break;
+                               }
+                       }
+
+                       ocelot_write_rix(ocelot,
+                                        BIT(ocelot->num_phys_ports) | mask,
+                                        ANA_PGID_PGID, PGID_SRC + port);
+               } else {
+                       /* Only the CPU port, this is compatible with link
+                        * aggregation.
+                        */
+                       ocelot_write_rix(ocelot,
+                                        BIT(ocelot->num_phys_ports),
+                                        ANA_PGID_PGID, PGID_SRC + port);
+               }
+       }
+
+       return 0;
+}
+
+static void ocelot_port_attr_ageing_set(struct ocelot_port *ocelot_port,
+                                       unsigned long ageing_clock_t)
+{
+       struct ocelot *ocelot = ocelot_port->ocelot;
+       unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
+       u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
+
+       ocelot_write(ocelot, ANA_AUTOAGE_AGE_PERIOD(ageing_time / 2),
+                    ANA_AUTOAGE);
+}
+
+static void ocelot_port_attr_mc_set(struct ocelot_port *port, bool mc)
+{
+       struct ocelot *ocelot = port->ocelot;
+       u32 val = ocelot_read_gix(ocelot, ANA_PORT_CPU_FWD_CFG,
+                                 port->chip_port);
+
+       if (mc)
+               val |= ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA |
+                      ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA |
+                      ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA;
+       else
+               val &= ~(ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA |
+                        ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA |
+                        ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA);
+
+       ocelot_write_gix(ocelot, val, ANA_PORT_CPU_FWD_CFG, port->chip_port);
+}
+
+static int ocelot_port_attr_set(struct net_device *dev,
+                               const struct switchdev_attr *attr,
+                               struct switchdev_trans *trans)
+{
+       struct ocelot_port *ocelot_port = netdev_priv(dev);
+       int err = 0;
+
+       switch (attr->id) {
+       case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+               ocelot_port_attr_stp_state_set(ocelot_port, trans,
+                                              attr->u.stp_state);
+               break;
+       case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
+               ocelot_port_attr_ageing_set(ocelot_port, attr->u.ageing_time);
+               break;
+       case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
+               ocelot_port_attr_mc_set(ocelot_port, !attr->u.mc_disabled);
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
+static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot,
+                                                    const unsigned char *addr,
+                                                    u16 vid)
+{
+       struct ocelot_multicast *mc;
+
+       list_for_each_entry(mc, &ocelot->multicast, list) {
+               if (ether_addr_equal(mc->addr, addr) && mc->vid == vid)
+                       return mc;
+       }
+
+       return NULL;
+}
+
+static int ocelot_port_obj_add_mdb(struct net_device *dev,
+                                  const struct switchdev_obj_port_mdb *mdb,
+                                  struct switchdev_trans *trans)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+       struct ocelot_multicast *mc;
+       unsigned char addr[ETH_ALEN];
+       u16 vid = mdb->vid;
+       bool new = false;
+
+       if (!vid)
+               vid = 1;
+
+       mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
+       if (!mc) {
+               mc = devm_kzalloc(ocelot->dev, sizeof(*mc), GFP_KERNEL);
+               if (!mc)
+                       return -ENOMEM;
+
+               memcpy(mc->addr, mdb->addr, ETH_ALEN);
+               mc->vid = vid;
+
+               list_add_tail(&mc->list, &ocelot->multicast);
+               new = true;
+       }
+
+       memcpy(addr, mc->addr, ETH_ALEN);
+       addr[0] = 0;
+
+       if (!new) {
+               addr[2] = mc->ports << 0;
+               addr[1] = mc->ports << 8;
+               ocelot_mact_forget(ocelot, addr, vid);
+       }
+
+       mc->ports |= BIT(port->chip_port);
+       addr[2] = mc->ports << 0;
+       addr[1] = mc->ports << 8;
+
+       return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4);
+}
+
+static int ocelot_port_obj_del_mdb(struct net_device *dev,
+                                  const struct switchdev_obj_port_mdb *mdb)
+{
+       struct ocelot_port *port = netdev_priv(dev);
+       struct ocelot *ocelot = port->ocelot;
+       struct ocelot_multicast *mc;
+       unsigned char addr[ETH_ALEN];
+       u16 vid = mdb->vid;
+
+       if (!vid)
+               vid = 1;
+
+       mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
+       if (!mc)
+               return -ENOENT;
+
+       memcpy(addr, mc->addr, ETH_ALEN);
+       addr[2] = mc->ports << 0;
+       addr[1] = mc->ports << 8;
+       addr[0] = 0;
+       ocelot_mact_forget(ocelot, addr, vid);
+
+       mc->ports &= ~BIT(port->chip_port);
+       if (!mc->ports) {
+               list_del(&mc->list);
+               devm_kfree(ocelot->dev, mc);
+               return 0;
+       }
+
+       addr[2] = mc->ports << 0;
+       addr[1] = mc->ports << 8;
+
+       return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4);
+}
+
+static int ocelot_port_obj_add(struct net_device *dev,
+                              const struct switchdev_obj *obj,
+                              struct switchdev_trans *trans)
+{
+       int ret = 0;
+
+       switch (obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_MDB:
+               ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj),
+                                             trans);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return ret;
+}
+
+static int ocelot_port_obj_del(struct net_device *dev,
+                              const struct switchdev_obj *obj)
+{
+       int ret = 0;
+
+       switch (obj->id) {
+       case SWITCHDEV_OBJ_ID_PORT_MDB:
+               ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return ret;
+}
+
+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,
+                                  struct net_device *bridge)
+{
+       struct ocelot *ocelot = ocelot_port->ocelot;
+
+       if (!ocelot->bridge_mask) {
+               ocelot->hw_bridge_dev = bridge;
+       } else {
+               if (ocelot->hw_bridge_dev != bridge)
+                       /* This is adding the port to a second bridge, this is
+                        * unsupported */
+                       return -ENODEV;
+       }
+
+       ocelot->bridge_mask |= BIT(ocelot_port->chip_port);
+
+       return 0;
+}
+
+static void ocelot_port_bridge_leave(struct ocelot_port *ocelot_port,
+                                    struct net_device *bridge)
+{
+       struct ocelot *ocelot = ocelot_port->ocelot;
+
+       ocelot->bridge_mask &= ~BIT(ocelot_port->chip_port);
+
+       if (!ocelot->bridge_mask)
+               ocelot->hw_bridge_dev = NULL;
+}
+
+/* Checks if the net_device instance given to us originate from our driver. */
+static bool ocelot_netdevice_dev_check(const struct net_device *dev)
+{
+       return dev->netdev_ops == &ocelot_port_netdev_ops;
+}
+
+static int ocelot_netdevice_port_event(struct net_device *dev,
+                                      unsigned long event,
+                                      struct netdev_notifier_changeupper_info *info)
+{
+       struct ocelot_port *ocelot_port = netdev_priv(dev);
+       int err = 0;
+
+       if (!ocelot_netdevice_dev_check(dev))
+               return 0;
+
+       switch (event) {
+       case NETDEV_CHANGEUPPER:
+               if (netif_is_bridge_master(info->upper_dev)) {
+                       if (info->linking)
+                               err = ocelot_port_bridge_join(ocelot_port,
+                                                             info->upper_dev);
+                       else
+                               ocelot_port_bridge_leave(ocelot_port,
+                                                        info->upper_dev);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return err;
+}
+
+static int ocelot_netdevice_event(struct notifier_block *unused,
+                                 unsigned long event, void *ptr)
+{
+       struct netdev_notifier_changeupper_info *info = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       int ret;
+
+       if (netif_is_lag_master(dev)) {
+               struct net_device *slave;
+               struct list_head *iter;
+
+               netdev_for_each_lower_dev(dev, slave, iter) {
+                       ret = ocelot_netdevice_port_event(slave, event, info);
+                       if (ret)
+                               goto notify;
+               }
+       } else {
+               ret = ocelot_netdevice_port_event(dev, event, info);
+       }
+
+notify:
+       return notifier_from_errno(ret);
+}
+
+struct notifier_block ocelot_netdevice_nb __read_mostly = {
+       .notifier_call = ocelot_netdevice_event,
+};
+EXPORT_SYMBOL(ocelot_netdevice_nb);
+
+int ocelot_probe_port(struct ocelot *ocelot, u8 port,
+                     void __iomem *regs,
+                     struct phy_device *phy)
+{
+       struct ocelot_port *ocelot_port;
+       struct net_device *dev;
+       int err;
+
+       dev = alloc_etherdev(sizeof(struct ocelot_port));
+       if (!dev)
+               return -ENOMEM;
+       SET_NETDEV_DEV(dev, ocelot->dev);
+       ocelot_port = netdev_priv(dev);
+       ocelot_port->dev = dev;
+       ocelot_port->ocelot = ocelot;
+       ocelot_port->regs = regs;
+       ocelot_port->chip_port = port;
+       ocelot_port->phy = phy;
+       INIT_LIST_HEAD(&ocelot_port->mc);
+       ocelot->ports[port] = ocelot_port;
+
+       dev->netdev_ops = &ocelot_port_netdev_ops;
+       dev->ethtool_ops = &ocelot_ethtool_ops;
+       dev->switchdev_ops = &ocelot_port_switchdev_ops;
+
+       memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN);
+       dev->dev_addr[ETH_ALEN - 1] += port;
+       ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, ocelot_port->pvid,
+                         ENTRYTYPE_LOCKED);
+
+       err = register_netdev(dev);
+       if (err) {
+               dev_err(ocelot->dev, "register_netdev failed\n");
+               goto err_register_netdev;
+       }
+
+       return 0;
+
+err_register_netdev:
+       free_netdev(dev);
+       return err;
+}
+EXPORT_SYMBOL(ocelot_probe_port);
+
+int ocelot_init(struct ocelot *ocelot)
+{
+       u32 port;
+       int i, cpu = ocelot->num_phys_ports;
+       char queue_name[32];
+
+       ocelot->stats = devm_kcalloc(ocelot->dev,
+                                    ocelot->num_phys_ports * ocelot->num_stats,
+                                    sizeof(u64), GFP_KERNEL);
+       if (!ocelot->stats)
+               return -ENOMEM;
+
+       mutex_init(&ocelot->stats_lock);
+       snprintf(queue_name, sizeof(queue_name), "%s-stats",
+                dev_name(ocelot->dev));
+       ocelot->stats_queue = create_singlethread_workqueue(queue_name);
+       if (!ocelot->stats_queue)
+               return -ENOMEM;
+
+       ocelot_mact_init(ocelot);
+       ocelot_vlan_init(ocelot);
+
+       for (port = 0; port < ocelot->num_phys_ports; port++) {
+               /* Clear all counters (5 groups) */
+               ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port) |
+                                    SYS_STAT_CFG_STAT_CLEAR_SHOT(0x7f),
+                            SYS_STAT_CFG);
+       }
+
+       /* Only use S-Tag */
+       ocelot_write(ocelot, ETH_P_8021AD, SYS_VLAN_ETYPE_CFG);
+
+       /* Aggregation mode */
+       ocelot_write(ocelot, ANA_AGGR_CFG_AC_SMAC_ENA |
+                            ANA_AGGR_CFG_AC_DMAC_ENA |
+                            ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA |
+                            ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA, ANA_AGGR_CFG);
+
+       /* Set MAC age time to default value. The entry is aged after
+        * 2*AGE_PERIOD
+        */
+       ocelot_write(ocelot,
+                    ANA_AUTOAGE_AGE_PERIOD(BR_DEFAULT_AGEING_TIME / 2 / HZ),
+                    ANA_AUTOAGE);
+
+       /* Disable learning for frames discarded by VLAN ingress filtering */
+       regmap_field_write(ocelot->regfields[ANA_ADVLEARN_VLAN_CHK], 1);
+
+       /* Setup frame ageing - fixed value "2 sec" - in 6.5 us units */
+       ocelot_write(ocelot, SYS_FRM_AGING_AGE_TX_ENA |
+                    SYS_FRM_AGING_MAX_AGE(307692), SYS_FRM_AGING);
+
+       /* Setup flooding PGIDs */
+       ocelot_write_rix(ocelot, ANA_FLOODING_FLD_MULTICAST(PGID_MC) |
+                        ANA_FLOODING_FLD_BROADCAST(PGID_MC) |
+                        ANA_FLOODING_FLD_UNICAST(PGID_UC),
+                        ANA_FLOODING, 0);
+       ocelot_write(ocelot, ANA_FLOODING_IPMC_FLD_MC6_DATA(PGID_MCIPV6) |
+                    ANA_FLOODING_IPMC_FLD_MC6_CTRL(PGID_MC) |
+                    ANA_FLOODING_IPMC_FLD_MC4_DATA(PGID_MCIPV4) |
+                    ANA_FLOODING_IPMC_FLD_MC4_CTRL(PGID_MC),
+                    ANA_FLOODING_IPMC);
+
+       for (port = 0; port < ocelot->num_phys_ports; port++) {
+               /* Transmit the frame to the local port. */
+               ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, port);
+               /* Do not forward BPDU frames to the front ports. */
+               ocelot_write_gix(ocelot,
+                                ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff),
+                                ANA_PORT_CPU_FWD_BPDU_CFG,
+                                port);
+               /* Ensure bridging is disabled */
+               ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_SRC + port);
+       }
+
+       /* Configure and enable the CPU port. */
+       ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu);
+       ocelot_write_rix(ocelot, BIT(cpu), ANA_PGID_PGID, PGID_CPU);
+       ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_RECV_ENA |
+                        ANA_PORT_PORT_CFG_PORTID_VAL(cpu),
+                        ANA_PORT_PORT_CFG, cpu);
+
+       /* Allow broadcast MAC frames. */
+       for (i = ocelot->num_phys_ports + 1; i < PGID_CPU; i++) {
+               u32 val = ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports - 1, 0));
+
+               ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i);
+       }
+       ocelot_write_rix(ocelot,
+                        ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)),
+                        ANA_PGID_PGID, PGID_MC);
+       ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV4);
+       ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV6);
+
+       /* CPU port Injection/Extraction configuration */
+       ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE |
+                        QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) |
+                        QSYS_SWITCH_PORT_MODE_PORT_ENA,
+                        QSYS_SWITCH_PORT_MODE, cpu);
+       ocelot_write_rix(ocelot, SYS_PORT_MODE_INCL_XTR_HDR(1) |
+                        SYS_PORT_MODE_INCL_INJ_HDR(1), SYS_PORT_MODE, cpu);
+       /* Allow manual injection via DEVCPU_QS registers, and byte swap these
+        * registers endianness.
+        */
+       ocelot_write_rix(ocelot, QS_INJ_GRP_CFG_BYTE_SWAP |
+                        QS_INJ_GRP_CFG_MODE(1), QS_INJ_GRP_CFG, 0);
+       ocelot_write_rix(ocelot, QS_XTR_GRP_CFG_BYTE_SWAP |
+                        QS_XTR_GRP_CFG_MODE(1), QS_XTR_GRP_CFG, 0);
+       ocelot_write(ocelot, ANA_CPUQ_CFG_CPUQ_MIRROR(2) |
+                    ANA_CPUQ_CFG_CPUQ_LRN(2) |
+                    ANA_CPUQ_CFG_CPUQ_MAC_COPY(2) |
+                    ANA_CPUQ_CFG_CPUQ_SRC_COPY(2) |
+                    ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE(2) |
+                    ANA_CPUQ_CFG_CPUQ_ALLBRIDGE(6) |
+                    ANA_CPUQ_CFG_CPUQ_IPMC_CTRL(6) |
+                    ANA_CPUQ_CFG_CPUQ_IGMP(6) |
+                    ANA_CPUQ_CFG_CPUQ_MLD(6), ANA_CPUQ_CFG);
+       for (i = 0; i < 16; i++)
+               ocelot_write_rix(ocelot, ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL(6) |
+                                ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL(6),
+                                ANA_CPUQ_8021_CFG, i);
+
+       INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats);
+       queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
+                          OCELOT_STATS_CHECK_DELAY);
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_init);
+
+void ocelot_deinit(struct ocelot *ocelot)
+{
+       destroy_workqueue(ocelot->stats_queue);
+       mutex_destroy(&ocelot->stats_lock);
+}
+EXPORT_SYMBOL(ocelot_deinit);
+
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
new file mode 100644 (file)
index 0000000..097bd12
--- /dev/null
@@ -0,0 +1,572 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_H_
+#define _MSCC_OCELOT_H_
+
+#include <linux/bitops.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "ocelot_ana.h"
+#include "ocelot_dev.h"
+#include "ocelot_hsio.h"
+#include "ocelot_qsys.h"
+#include "ocelot_rew.h"
+#include "ocelot_sys.h"
+#include "ocelot_qs.h"
+
+#define PGID_AGGR    64
+#define PGID_SRC     80
+
+/* Reserved PGIDs */
+#define PGID_CPU     (PGID_AGGR - 5)
+#define PGID_UC      (PGID_AGGR - 4)
+#define PGID_MC      (PGID_AGGR - 3)
+#define PGID_MCIPV4  (PGID_AGGR - 2)
+#define PGID_MCIPV6  (PGID_AGGR - 1)
+
+#define OCELOT_BUFFER_CELL_SZ 60
+
+#define OCELOT_STATS_CHECK_DELAY (2 * HZ)
+
+#define IFH_LEN 4
+
+struct frame_info {
+       u32 len;
+       u16 port;
+       u16 vid;
+       u8 cpuq;
+       u8 tag_type;
+};
+
+#define IFH_INJ_BYPASS BIT(31)
+#define IFH_INJ_POP_CNT_DISABLE (3 << 28)
+
+#define IFH_TAG_TYPE_C 0
+#define IFH_TAG_TYPE_S 1
+
+#define OCELOT_SPEED_2500 0
+#define OCELOT_SPEED_1000 1
+#define OCELOT_SPEED_100  2
+#define OCELOT_SPEED_10   3
+
+#define TARGET_OFFSET 24
+#define REG_MASK GENMASK(TARGET_OFFSET - 1, 0)
+#define REG(reg, offset) [reg & REG_MASK] = offset
+
+enum ocelot_target {
+       ANA = 1,
+       QS,
+       QSYS,
+       REW,
+       SYS,
+       HSIO,
+       TARGET_MAX,
+};
+
+enum ocelot_reg {
+       ANA_ADVLEARN = ANA << TARGET_OFFSET,
+       ANA_VLANMASK,
+       ANA_PORT_B_DOMAIN,
+       ANA_ANAGEFIL,
+       ANA_ANEVENTS,
+       ANA_STORMLIMIT_BURST,
+       ANA_STORMLIMIT_CFG,
+       ANA_ISOLATED_PORTS,
+       ANA_COMMUNITY_PORTS,
+       ANA_AUTOAGE,
+       ANA_MACTOPTIONS,
+       ANA_LEARNDISC,
+       ANA_AGENCTRL,
+       ANA_MIRRORPORTS,
+       ANA_EMIRRORPORTS,
+       ANA_FLOODING,
+       ANA_FLOODING_IPMC,
+       ANA_SFLOW_CFG,
+       ANA_PORT_MODE,
+       ANA_CUT_THRU_CFG,
+       ANA_PGID_PGID,
+       ANA_TABLES_ANMOVED,
+       ANA_TABLES_MACHDATA,
+       ANA_TABLES_MACLDATA,
+       ANA_TABLES_STREAMDATA,
+       ANA_TABLES_MACACCESS,
+       ANA_TABLES_MACTINDX,
+       ANA_TABLES_VLANACCESS,
+       ANA_TABLES_VLANTIDX,
+       ANA_TABLES_ISDXACCESS,
+       ANA_TABLES_ISDXTIDX,
+       ANA_TABLES_ENTRYLIM,
+       ANA_TABLES_PTP_ID_HIGH,
+       ANA_TABLES_PTP_ID_LOW,
+       ANA_TABLES_STREAMACCESS,
+       ANA_TABLES_STREAMTIDX,
+       ANA_TABLES_SEQ_HISTORY,
+       ANA_TABLES_SEQ_MASK,
+       ANA_TABLES_SFID_MASK,
+       ANA_TABLES_SFIDACCESS,
+       ANA_TABLES_SFIDTIDX,
+       ANA_MSTI_STATE,
+       ANA_OAM_UPM_LM_CNT,
+       ANA_SG_ACCESS_CTRL,
+       ANA_SG_CONFIG_REG_1,
+       ANA_SG_CONFIG_REG_2,
+       ANA_SG_CONFIG_REG_3,
+       ANA_SG_CONFIG_REG_4,
+       ANA_SG_CONFIG_REG_5,
+       ANA_SG_GCL_GS_CONFIG,
+       ANA_SG_GCL_TI_CONFIG,
+       ANA_SG_STATUS_REG_1,
+       ANA_SG_STATUS_REG_2,
+       ANA_SG_STATUS_REG_3,
+       ANA_PORT_VLAN_CFG,
+       ANA_PORT_DROP_CFG,
+       ANA_PORT_QOS_CFG,
+       ANA_PORT_VCAP_CFG,
+       ANA_PORT_VCAP_S1_KEY_CFG,
+       ANA_PORT_VCAP_S2_CFG,
+       ANA_PORT_PCP_DEI_MAP,
+       ANA_PORT_CPU_FWD_CFG,
+       ANA_PORT_CPU_FWD_BPDU_CFG,
+       ANA_PORT_CPU_FWD_GARP_CFG,
+       ANA_PORT_CPU_FWD_CCM_CFG,
+       ANA_PORT_PORT_CFG,
+       ANA_PORT_POL_CFG,
+       ANA_PORT_PTP_CFG,
+       ANA_PORT_PTP_DLY1_CFG,
+       ANA_PORT_PTP_DLY2_CFG,
+       ANA_PORT_SFID_CFG,
+       ANA_PFC_PFC_CFG,
+       ANA_PFC_PFC_TIMER,
+       ANA_IPT_OAM_MEP_CFG,
+       ANA_IPT_IPT,
+       ANA_PPT_PPT,
+       ANA_FID_MAP_FID_MAP,
+       ANA_AGGR_CFG,
+       ANA_CPUQ_CFG,
+       ANA_CPUQ_CFG2,
+       ANA_CPUQ_8021_CFG,
+       ANA_DSCP_CFG,
+       ANA_DSCP_REWR_CFG,
+       ANA_VCAP_RNG_TYPE_CFG,
+       ANA_VCAP_RNG_VAL_CFG,
+       ANA_VRAP_CFG,
+       ANA_VRAP_HDR_DATA,
+       ANA_VRAP_HDR_MASK,
+       ANA_DISCARD_CFG,
+       ANA_FID_CFG,
+       ANA_POL_PIR_CFG,
+       ANA_POL_CIR_CFG,
+       ANA_POL_MODE_CFG,
+       ANA_POL_PIR_STATE,
+       ANA_POL_CIR_STATE,
+       ANA_POL_STATE,
+       ANA_POL_FLOWC,
+       ANA_POL_HYST,
+       ANA_POL_MISC_CFG,
+       QS_XTR_GRP_CFG = QS << TARGET_OFFSET,
+       QS_XTR_RD,
+       QS_XTR_FRM_PRUNING,
+       QS_XTR_FLUSH,
+       QS_XTR_DATA_PRESENT,
+       QS_XTR_CFG,
+       QS_INJ_GRP_CFG,
+       QS_INJ_WR,
+       QS_INJ_CTRL,
+       QS_INJ_STATUS,
+       QS_INJ_ERR,
+       QS_INH_DBG,
+       QSYS_PORT_MODE = QSYS << TARGET_OFFSET,
+       QSYS_SWITCH_PORT_MODE,
+       QSYS_STAT_CNT_CFG,
+       QSYS_EEE_CFG,
+       QSYS_EEE_THRES,
+       QSYS_IGR_NO_SHARING,
+       QSYS_EGR_NO_SHARING,
+       QSYS_SW_STATUS,
+       QSYS_EXT_CPU_CFG,
+       QSYS_PAD_CFG,
+       QSYS_CPU_GROUP_MAP,
+       QSYS_QMAP,
+       QSYS_ISDX_SGRP,
+       QSYS_TIMED_FRAME_ENTRY,
+       QSYS_TFRM_MISC,
+       QSYS_TFRM_PORT_DLY,
+       QSYS_TFRM_TIMER_CFG_1,
+       QSYS_TFRM_TIMER_CFG_2,
+       QSYS_TFRM_TIMER_CFG_3,
+       QSYS_TFRM_TIMER_CFG_4,
+       QSYS_TFRM_TIMER_CFG_5,
+       QSYS_TFRM_TIMER_CFG_6,
+       QSYS_TFRM_TIMER_CFG_7,
+       QSYS_TFRM_TIMER_CFG_8,
+       QSYS_RED_PROFILE,
+       QSYS_RES_QOS_MODE,
+       QSYS_RES_CFG,
+       QSYS_RES_STAT,
+       QSYS_EGR_DROP_MODE,
+       QSYS_EQ_CTRL,
+       QSYS_EVENTS_CORE,
+       QSYS_QMAXSDU_CFG_0,
+       QSYS_QMAXSDU_CFG_1,
+       QSYS_QMAXSDU_CFG_2,
+       QSYS_QMAXSDU_CFG_3,
+       QSYS_QMAXSDU_CFG_4,
+       QSYS_QMAXSDU_CFG_5,
+       QSYS_QMAXSDU_CFG_6,
+       QSYS_QMAXSDU_CFG_7,
+       QSYS_PREEMPTION_CFG,
+       QSYS_CIR_CFG,
+       QSYS_EIR_CFG,
+       QSYS_SE_CFG,
+       QSYS_SE_DWRR_CFG,
+       QSYS_SE_CONNECT,
+       QSYS_SE_DLB_SENSE,
+       QSYS_CIR_STATE,
+       QSYS_EIR_STATE,
+       QSYS_SE_STATE,
+       QSYS_HSCH_MISC_CFG,
+       QSYS_TAG_CONFIG,
+       QSYS_TAS_PARAM_CFG_CTRL,
+       QSYS_PORT_MAX_SDU,
+       QSYS_PARAM_CFG_REG_1,
+       QSYS_PARAM_CFG_REG_2,
+       QSYS_PARAM_CFG_REG_3,
+       QSYS_PARAM_CFG_REG_4,
+       QSYS_PARAM_CFG_REG_5,
+       QSYS_GCL_CFG_REG_1,
+       QSYS_GCL_CFG_REG_2,
+       QSYS_PARAM_STATUS_REG_1,
+       QSYS_PARAM_STATUS_REG_2,
+       QSYS_PARAM_STATUS_REG_3,
+       QSYS_PARAM_STATUS_REG_4,
+       QSYS_PARAM_STATUS_REG_5,
+       QSYS_PARAM_STATUS_REG_6,
+       QSYS_PARAM_STATUS_REG_7,
+       QSYS_PARAM_STATUS_REG_8,
+       QSYS_PARAM_STATUS_REG_9,
+       QSYS_GCL_STATUS_REG_1,
+       QSYS_GCL_STATUS_REG_2,
+       REW_PORT_VLAN_CFG = REW << TARGET_OFFSET,
+       REW_TAG_CFG,
+       REW_PORT_CFG,
+       REW_DSCP_CFG,
+       REW_PCP_DEI_QOS_MAP_CFG,
+       REW_PTP_CFG,
+       REW_PTP_DLY1_CFG,
+       REW_RED_TAG_CFG,
+       REW_DSCP_REMAP_DP1_CFG,
+       REW_DSCP_REMAP_CFG,
+       REW_STAT_CFG,
+       REW_REW_STICKY,
+       REW_PPT,
+       SYS_COUNT_RX_OCTETS = SYS << TARGET_OFFSET,
+       SYS_COUNT_RX_UNICAST,
+       SYS_COUNT_RX_MULTICAST,
+       SYS_COUNT_RX_BROADCAST,
+       SYS_COUNT_RX_SHORTS,
+       SYS_COUNT_RX_FRAGMENTS,
+       SYS_COUNT_RX_JABBERS,
+       SYS_COUNT_RX_CRC_ALIGN_ERRS,
+       SYS_COUNT_RX_SYM_ERRS,
+       SYS_COUNT_RX_64,
+       SYS_COUNT_RX_65_127,
+       SYS_COUNT_RX_128_255,
+       SYS_COUNT_RX_256_1023,
+       SYS_COUNT_RX_1024_1526,
+       SYS_COUNT_RX_1527_MAX,
+       SYS_COUNT_RX_PAUSE,
+       SYS_COUNT_RX_CONTROL,
+       SYS_COUNT_RX_LONGS,
+       SYS_COUNT_RX_CLASSIFIED_DROPS,
+       SYS_COUNT_TX_OCTETS,
+       SYS_COUNT_TX_UNICAST,
+       SYS_COUNT_TX_MULTICAST,
+       SYS_COUNT_TX_BROADCAST,
+       SYS_COUNT_TX_COLLISION,
+       SYS_COUNT_TX_DROPS,
+       SYS_COUNT_TX_PAUSE,
+       SYS_COUNT_TX_64,
+       SYS_COUNT_TX_65_127,
+       SYS_COUNT_TX_128_511,
+       SYS_COUNT_TX_512_1023,
+       SYS_COUNT_TX_1024_1526,
+       SYS_COUNT_TX_1527_MAX,
+       SYS_COUNT_TX_AGING,
+       SYS_RESET_CFG,
+       SYS_SR_ETYPE_CFG,
+       SYS_VLAN_ETYPE_CFG,
+       SYS_PORT_MODE,
+       SYS_FRONT_PORT_MODE,
+       SYS_FRM_AGING,
+       SYS_STAT_CFG,
+       SYS_SW_STATUS,
+       SYS_MISC_CFG,
+       SYS_REW_MAC_HIGH_CFG,
+       SYS_REW_MAC_LOW_CFG,
+       SYS_TIMESTAMP_OFFSET,
+       SYS_CMID,
+       SYS_PAUSE_CFG,
+       SYS_PAUSE_TOT_CFG,
+       SYS_ATOP,
+       SYS_ATOP_TOT_CFG,
+       SYS_MAC_FC_CFG,
+       SYS_MMGT,
+       SYS_MMGT_FAST,
+       SYS_EVENTS_DIF,
+       SYS_EVENTS_CORE,
+       SYS_CNT,
+       SYS_PTP_STATUS,
+       SYS_PTP_TXSTAMP,
+       SYS_PTP_NXT,
+       SYS_PTP_CFG,
+       SYS_RAM_INIT,
+       SYS_CM_ADDR,
+       SYS_CM_DATA_WR,
+       SYS_CM_DATA_RD,
+       SYS_CM_OP,
+       SYS_CM_DATA,
+       HSIO_PLL5G_CFG0 = HSIO << TARGET_OFFSET,
+       HSIO_PLL5G_CFG1,
+       HSIO_PLL5G_CFG2,
+       HSIO_PLL5G_CFG3,
+       HSIO_PLL5G_CFG4,
+       HSIO_PLL5G_CFG5,
+       HSIO_PLL5G_CFG6,
+       HSIO_PLL5G_STATUS0,
+       HSIO_PLL5G_STATUS1,
+       HSIO_PLL5G_BIST_CFG0,
+       HSIO_PLL5G_BIST_CFG1,
+       HSIO_PLL5G_BIST_CFG2,
+       HSIO_PLL5G_BIST_STAT0,
+       HSIO_PLL5G_BIST_STAT1,
+       HSIO_RCOMP_CFG0,
+       HSIO_RCOMP_STATUS,
+       HSIO_SYNC_ETH_CFG,
+       HSIO_SYNC_ETH_PLL_CFG,
+       HSIO_S1G_DES_CFG,
+       HSIO_S1G_IB_CFG,
+       HSIO_S1G_OB_CFG,
+       HSIO_S1G_SER_CFG,
+       HSIO_S1G_COMMON_CFG,
+       HSIO_S1G_PLL_CFG,
+       HSIO_S1G_PLL_STATUS,
+       HSIO_S1G_DFT_CFG0,
+       HSIO_S1G_DFT_CFG1,
+       HSIO_S1G_DFT_CFG2,
+       HSIO_S1G_TP_CFG,
+       HSIO_S1G_RC_PLL_BIST_CFG,
+       HSIO_S1G_MISC_CFG,
+       HSIO_S1G_DFT_STATUS,
+       HSIO_S1G_MISC_STATUS,
+       HSIO_MCB_S1G_ADDR_CFG,
+       HSIO_S6G_DIG_CFG,
+       HSIO_S6G_DFT_CFG0,
+       HSIO_S6G_DFT_CFG1,
+       HSIO_S6G_DFT_CFG2,
+       HSIO_S6G_TP_CFG0,
+       HSIO_S6G_TP_CFG1,
+       HSIO_S6G_RC_PLL_BIST_CFG,
+       HSIO_S6G_MISC_CFG,
+       HSIO_S6G_OB_ANEG_CFG,
+       HSIO_S6G_DFT_STATUS,
+       HSIO_S6G_ERR_CNT,
+       HSIO_S6G_MISC_STATUS,
+       HSIO_S6G_DES_CFG,
+       HSIO_S6G_IB_CFG,
+       HSIO_S6G_IB_CFG1,
+       HSIO_S6G_IB_CFG2,
+       HSIO_S6G_IB_CFG3,
+       HSIO_S6G_IB_CFG4,
+       HSIO_S6G_IB_CFG5,
+       HSIO_S6G_OB_CFG,
+       HSIO_S6G_OB_CFG1,
+       HSIO_S6G_SER_CFG,
+       HSIO_S6G_COMMON_CFG,
+       HSIO_S6G_PLL_CFG,
+       HSIO_S6G_ACJTAG_CFG,
+       HSIO_S6G_GP_CFG,
+       HSIO_S6G_IB_STATUS0,
+       HSIO_S6G_IB_STATUS1,
+       HSIO_S6G_ACJTAG_STATUS,
+       HSIO_S6G_PLL_STATUS,
+       HSIO_S6G_REVID,
+       HSIO_MCB_S6G_ADDR_CFG,
+       HSIO_HW_CFG,
+       HSIO_HW_QSGMII_CFG,
+       HSIO_HW_QSGMII_STAT,
+       HSIO_CLK_CFG,
+       HSIO_TEMP_SENSOR_CTRL,
+       HSIO_TEMP_SENSOR_CFG,
+       HSIO_TEMP_SENSOR_STAT,
+};
+
+enum ocelot_regfield {
+       ANA_ADVLEARN_VLAN_CHK,
+       ANA_ADVLEARN_LEARN_MIRROR,
+       ANA_ANEVENTS_FLOOD_DISCARD,
+       ANA_ANEVENTS_MSTI_DROP,
+       ANA_ANEVENTS_ACLKILL,
+       ANA_ANEVENTS_ACLUSED,
+       ANA_ANEVENTS_AUTOAGE,
+       ANA_ANEVENTS_VS2TTL1,
+       ANA_ANEVENTS_STORM_DROP,
+       ANA_ANEVENTS_LEARN_DROP,
+       ANA_ANEVENTS_AGED_ENTRY,
+       ANA_ANEVENTS_CPU_LEARN_FAILED,
+       ANA_ANEVENTS_AUTO_LEARN_FAILED,
+       ANA_ANEVENTS_LEARN_REMOVE,
+       ANA_ANEVENTS_AUTO_LEARNED,
+       ANA_ANEVENTS_AUTO_MOVED,
+       ANA_ANEVENTS_DROPPED,
+       ANA_ANEVENTS_CLASSIFIED_DROP,
+       ANA_ANEVENTS_CLASSIFIED_COPY,
+       ANA_ANEVENTS_VLAN_DISCARD,
+       ANA_ANEVENTS_FWD_DISCARD,
+       ANA_ANEVENTS_MULTICAST_FLOOD,
+       ANA_ANEVENTS_UNICAST_FLOOD,
+       ANA_ANEVENTS_DEST_KNOWN,
+       ANA_ANEVENTS_BUCKET3_MATCH,
+       ANA_ANEVENTS_BUCKET2_MATCH,
+       ANA_ANEVENTS_BUCKET1_MATCH,
+       ANA_ANEVENTS_BUCKET0_MATCH,
+       ANA_ANEVENTS_CPU_OPERATION,
+       ANA_ANEVENTS_DMAC_LOOKUP,
+       ANA_ANEVENTS_SMAC_LOOKUP,
+       ANA_ANEVENTS_SEQ_GEN_ERR_0,
+       ANA_ANEVENTS_SEQ_GEN_ERR_1,
+       ANA_TABLES_MACACCESS_B_DOM,
+       ANA_TABLES_MACTINDX_BUCKET,
+       ANA_TABLES_MACTINDX_M_INDEX,
+       QSYS_TIMED_FRAME_ENTRY_TFRM_VLD,
+       QSYS_TIMED_FRAME_ENTRY_TFRM_FP,
+       QSYS_TIMED_FRAME_ENTRY_TFRM_PORTNO,
+       QSYS_TIMED_FRAME_ENTRY_TFRM_TM_SEL,
+       QSYS_TIMED_FRAME_ENTRY_TFRM_TM_T,
+       SYS_RESET_CFG_CORE_ENA,
+       SYS_RESET_CFG_MEM_ENA,
+       SYS_RESET_CFG_MEM_INIT,
+       REGFIELD_MAX
+};
+
+struct ocelot_multicast {
+       struct list_head list;
+       unsigned char addr[ETH_ALEN];
+       u16 vid;
+       u16 ports;
+};
+
+struct ocelot_port;
+
+struct ocelot_stat_layout {
+       u32 offset;
+       char name[ETH_GSTRING_LEN];
+};
+
+struct ocelot {
+       struct device *dev;
+
+       struct regmap *targets[TARGET_MAX];
+       struct regmap_field *regfields[REGFIELD_MAX];
+       const u32 *const *map;
+       const struct ocelot_stat_layout *stats_layout;
+       unsigned int num_stats;
+
+       u8 base_mac[ETH_ALEN];
+
+       struct net_device *hw_bridge_dev;
+       u16 bridge_mask;
+       u16 bridge_fwd_mask;
+
+       struct workqueue_struct *ocelot_owq;
+
+       int shared_queue_sz;
+
+       u8 num_phys_ports;
+       u8 num_cpu_ports;
+       struct ocelot_port **ports;
+
+       u16 lags[16];
+
+       /* Keep track of the vlan port masks */
+       u32 vlan_mask[VLAN_N_VID];
+
+       struct list_head multicast;
+
+       /* Workqueue to check statistics for overflow with its lock */
+       struct mutex stats_lock;
+       u64 *stats;
+       struct delayed_work stats_work;
+       struct workqueue_struct *stats_queue;
+};
+
+struct ocelot_port {
+       struct net_device *dev;
+       struct ocelot *ocelot;
+       struct phy_device *phy;
+       void __iomem *regs;
+       u8 chip_port;
+       /* Keep a track of the mc addresses added to the mac table, so that they
+        * can be removed when needed.
+        */
+       struct list_head mc;
+
+       /* Ingress default VLAN (pvid) */
+       u16 pvid;
+
+       /* Egress default VLAN (vid) */
+       u16 vid;
+
+       u8 vlan_aware;
+
+       u64 *stats;
+};
+
+u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset);
+#define ocelot_read_ix(ocelot, reg, gi, ri) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri))
+#define ocelot_read_gix(ocelot, reg, gi) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi))
+#define ocelot_read_rix(ocelot, reg, ri) __ocelot_read_ix(ocelot, reg, reg##_RSZ * (ri))
+#define ocelot_read(ocelot, reg) __ocelot_read_ix(ocelot, reg, 0)
+
+void __ocelot_write_ix(struct ocelot *ocelot, u32 val, u32 reg, u32 offset);
+#define ocelot_write_ix(ocelot, val, reg, gi, ri) __ocelot_write_ix(ocelot, val, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri))
+#define ocelot_write_gix(ocelot, val, reg, gi) __ocelot_write_ix(ocelot, val, reg, reg##_GSZ * (gi))
+#define ocelot_write_rix(ocelot, val, reg, ri) __ocelot_write_ix(ocelot, val, reg, reg##_RSZ * (ri))
+#define ocelot_write(ocelot, val, reg) __ocelot_write_ix(ocelot, val, reg, 0)
+
+void __ocelot_rmw_ix(struct ocelot *ocelot, u32 val, u32 reg, u32 mask,
+                    u32 offset);
+#define ocelot_rmw_ix(ocelot, val, m, reg, gi, ri) __ocelot_rmw_ix(ocelot, val, m, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri))
+#define ocelot_rmw_gix(ocelot, val, m, reg, gi) __ocelot_rmw_ix(ocelot, val, m, reg, reg##_GSZ * (gi))
+#define ocelot_rmw_rix(ocelot, val, m, reg, ri) __ocelot_rmw_ix(ocelot, val, m, reg, reg##_RSZ * (ri))
+#define ocelot_rmw(ocelot, val, m, reg) __ocelot_rmw_ix(ocelot, val, m, reg, 0)
+
+u32 ocelot_port_readl(struct ocelot_port *port, u32 reg);
+void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg);
+
+int ocelot_regfields_init(struct ocelot *ocelot,
+                         const struct reg_field *const regfields);
+struct regmap *ocelot_io_platform_init(struct ocelot *ocelot,
+                                      struct platform_device *pdev,
+                                      const char *name);
+
+#define ocelot_field_write(ocelot, reg, val) regmap_field_write((ocelot)->regfields[(reg)], (val))
+#define ocelot_field_read(ocelot, reg, val) regmap_field_read((ocelot)->regfields[(reg)], (val))
+
+int ocelot_init(struct ocelot *ocelot);
+void ocelot_deinit(struct ocelot *ocelot);
+int ocelot_chip_init(struct ocelot *ocelot);
+int ocelot_probe_port(struct ocelot *ocelot, u8 port,
+                     void __iomem *regs,
+                     struct phy_device *phy);
+
+extern struct notifier_block ocelot_netdevice_nb;
+
+#endif
diff --git a/drivers/net/ethernet/mscc/ocelot_ana.h b/drivers/net/ethernet/mscc/ocelot_ana.h
new file mode 100644 (file)
index 0000000..841c6ec
--- /dev/null
@@ -0,0 +1,625 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_ANA_H_
+#define _MSCC_OCELOT_ANA_H_
+
+#define ANA_ANAGEFIL_B_DOM_EN                             BIT(22)
+#define ANA_ANAGEFIL_B_DOM_VAL                            BIT(21)
+#define ANA_ANAGEFIL_AGE_LOCKED                           BIT(20)
+#define ANA_ANAGEFIL_PID_EN                               BIT(19)
+#define ANA_ANAGEFIL_PID_VAL(x)                           (((x) << 14) & GENMASK(18, 14))
+#define ANA_ANAGEFIL_PID_VAL_M                            GENMASK(18, 14)
+#define ANA_ANAGEFIL_PID_VAL_X(x)                         (((x) & GENMASK(18, 14)) >> 14)
+#define ANA_ANAGEFIL_VID_EN                               BIT(13)
+#define ANA_ANAGEFIL_VID_VAL(x)                           ((x) & GENMASK(12, 0))
+#define ANA_ANAGEFIL_VID_VAL_M                            GENMASK(12, 0)
+
+#define ANA_STORMLIMIT_CFG_RSZ                            0x4
+
+#define ANA_STORMLIMIT_CFG_STORM_RATE(x)                  (((x) << 3) & GENMASK(6, 3))
+#define ANA_STORMLIMIT_CFG_STORM_RATE_M                   GENMASK(6, 3)
+#define ANA_STORMLIMIT_CFG_STORM_RATE_X(x)                (((x) & GENMASK(6, 3)) >> 3)
+#define ANA_STORMLIMIT_CFG_STORM_UNIT                     BIT(2)
+#define ANA_STORMLIMIT_CFG_STORM_MODE(x)                  ((x) & GENMASK(1, 0))
+#define ANA_STORMLIMIT_CFG_STORM_MODE_M                   GENMASK(1, 0)
+
+#define ANA_AUTOAGE_AGE_FAST                              BIT(21)
+#define ANA_AUTOAGE_AGE_PERIOD(x)                         (((x) << 1) & GENMASK(20, 1))
+#define ANA_AUTOAGE_AGE_PERIOD_M                          GENMASK(20, 1)
+#define ANA_AUTOAGE_AGE_PERIOD_X(x)                       (((x) & GENMASK(20, 1)) >> 1)
+#define ANA_AUTOAGE_AUTOAGE_LOCKED                        BIT(0)
+
+#define ANA_MACTOPTIONS_REDUCED_TABLE                     BIT(1)
+#define ANA_MACTOPTIONS_SHADOW                            BIT(0)
+
+#define ANA_AGENCTRL_FID_MASK(x)                          (((x) << 12) & GENMASK(23, 12))
+#define ANA_AGENCTRL_FID_MASK_M                           GENMASK(23, 12)
+#define ANA_AGENCTRL_FID_MASK_X(x)                        (((x) & GENMASK(23, 12)) >> 12)
+#define ANA_AGENCTRL_IGNORE_DMAC_FLAGS                    BIT(11)
+#define ANA_AGENCTRL_IGNORE_SMAC_FLAGS                    BIT(10)
+#define ANA_AGENCTRL_FLOOD_SPECIAL                        BIT(9)
+#define ANA_AGENCTRL_FLOOD_IGNORE_VLAN                    BIT(8)
+#define ANA_AGENCTRL_MIRROR_CPU                           BIT(7)
+#define ANA_AGENCTRL_LEARN_CPU_COPY                       BIT(6)
+#define ANA_AGENCTRL_LEARN_FWD_KILL                       BIT(5)
+#define ANA_AGENCTRL_LEARN_IGNORE_VLAN                    BIT(4)
+#define ANA_AGENCTRL_CPU_CPU_KILL_ENA                     BIT(3)
+#define ANA_AGENCTRL_GREEN_COUNT_MODE                     BIT(2)
+#define ANA_AGENCTRL_YELLOW_COUNT_MODE                    BIT(1)
+#define ANA_AGENCTRL_RED_COUNT_MODE                       BIT(0)
+
+#define ANA_FLOODING_RSZ                                  0x4
+
+#define ANA_FLOODING_FLD_UNICAST(x)                       (((x) << 12) & GENMASK(17, 12))
+#define ANA_FLOODING_FLD_UNICAST_M                        GENMASK(17, 12)
+#define ANA_FLOODING_FLD_UNICAST_X(x)                     (((x) & GENMASK(17, 12)) >> 12)
+#define ANA_FLOODING_FLD_BROADCAST(x)                     (((x) << 6) & GENMASK(11, 6))
+#define ANA_FLOODING_FLD_BROADCAST_M                      GENMASK(11, 6)
+#define ANA_FLOODING_FLD_BROADCAST_X(x)                   (((x) & GENMASK(11, 6)) >> 6)
+#define ANA_FLOODING_FLD_MULTICAST(x)                     ((x) & GENMASK(5, 0))
+#define ANA_FLOODING_FLD_MULTICAST_M                      GENMASK(5, 0)
+
+#define ANA_FLOODING_IPMC_FLD_MC4_CTRL(x)                 (((x) << 18) & GENMASK(23, 18))
+#define ANA_FLOODING_IPMC_FLD_MC4_CTRL_M                  GENMASK(23, 18)
+#define ANA_FLOODING_IPMC_FLD_MC4_CTRL_X(x)               (((x) & GENMASK(23, 18)) >> 18)
+#define ANA_FLOODING_IPMC_FLD_MC4_DATA(x)                 (((x) << 12) & GENMASK(17, 12))
+#define ANA_FLOODING_IPMC_FLD_MC4_DATA_M                  GENMASK(17, 12)
+#define ANA_FLOODING_IPMC_FLD_MC4_DATA_X(x)               (((x) & GENMASK(17, 12)) >> 12)
+#define ANA_FLOODING_IPMC_FLD_MC6_CTRL(x)                 (((x) << 6) & GENMASK(11, 6))
+#define ANA_FLOODING_IPMC_FLD_MC6_CTRL_M                  GENMASK(11, 6)
+#define ANA_FLOODING_IPMC_FLD_MC6_CTRL_X(x)               (((x) & GENMASK(11, 6)) >> 6)
+#define ANA_FLOODING_IPMC_FLD_MC6_DATA(x)                 ((x) & GENMASK(5, 0))
+#define ANA_FLOODING_IPMC_FLD_MC6_DATA_M                  GENMASK(5, 0)
+
+#define ANA_SFLOW_CFG_RSZ                                 0x4
+
+#define ANA_SFLOW_CFG_SF_RATE(x)                          (((x) << 2) & GENMASK(13, 2))
+#define ANA_SFLOW_CFG_SF_RATE_M                           GENMASK(13, 2)
+#define ANA_SFLOW_CFG_SF_RATE_X(x)                        (((x) & GENMASK(13, 2)) >> 2)
+#define ANA_SFLOW_CFG_SF_SAMPLE_RX                        BIT(1)
+#define ANA_SFLOW_CFG_SF_SAMPLE_TX                        BIT(0)
+
+#define ANA_PORT_MODE_RSZ                                 0x4
+
+#define ANA_PORT_MODE_REDTAG_PARSE_CFG                    BIT(3)
+#define ANA_PORT_MODE_VLAN_PARSE_CFG(x)                   (((x) << 1) & GENMASK(2, 1))
+#define ANA_PORT_MODE_VLAN_PARSE_CFG_M                    GENMASK(2, 1)
+#define ANA_PORT_MODE_VLAN_PARSE_CFG_X(x)                 (((x) & GENMASK(2, 1)) >> 1)
+#define ANA_PORT_MODE_L3_PARSE_CFG                        BIT(0)
+
+#define ANA_CUT_THRU_CFG_RSZ                              0x4
+
+#define ANA_PGID_PGID_RSZ                                 0x4
+
+#define ANA_PGID_PGID_PGID(x)                             ((x) & GENMASK(11, 0))
+#define ANA_PGID_PGID_PGID_M                              GENMASK(11, 0)
+#define ANA_PGID_PGID_CPUQ_DST_PGID(x)                    (((x) << 27) & GENMASK(29, 27))
+#define ANA_PGID_PGID_CPUQ_DST_PGID_M                     GENMASK(29, 27)
+#define ANA_PGID_PGID_CPUQ_DST_PGID_X(x)                  (((x) & GENMASK(29, 27)) >> 27)
+
+#define ANA_TABLES_MACHDATA_VID(x)                        (((x) << 16) & GENMASK(28, 16))
+#define ANA_TABLES_MACHDATA_VID_M                         GENMASK(28, 16)
+#define ANA_TABLES_MACHDATA_VID_X(x)                      (((x) & GENMASK(28, 16)) >> 16)
+#define ANA_TABLES_MACHDATA_MACHDATA(x)                   ((x) & GENMASK(15, 0))
+#define ANA_TABLES_MACHDATA_MACHDATA_M                    GENMASK(15, 0)
+
+#define ANA_TABLES_STREAMDATA_SSID_VALID                  BIT(16)
+#define ANA_TABLES_STREAMDATA_SSID(x)                     (((x) << 9) & GENMASK(15, 9))
+#define ANA_TABLES_STREAMDATA_SSID_M                      GENMASK(15, 9)
+#define ANA_TABLES_STREAMDATA_SSID_X(x)                   (((x) & GENMASK(15, 9)) >> 9)
+#define ANA_TABLES_STREAMDATA_SFID_VALID                  BIT(8)
+#define ANA_TABLES_STREAMDATA_SFID(x)                     ((x) & GENMASK(7, 0))
+#define ANA_TABLES_STREAMDATA_SFID_M                      GENMASK(7, 0)
+
+#define ANA_TABLES_MACACCESS_MAC_CPU_COPY                 BIT(15)
+#define ANA_TABLES_MACACCESS_SRC_KILL                     BIT(14)
+#define ANA_TABLES_MACACCESS_IGNORE_VLAN                  BIT(13)
+#define ANA_TABLES_MACACCESS_AGED_FLAG                    BIT(12)
+#define ANA_TABLES_MACACCESS_VALID                        BIT(11)
+#define ANA_TABLES_MACACCESS_ENTRYTYPE(x)                 (((x) << 9) & GENMASK(10, 9))
+#define ANA_TABLES_MACACCESS_ENTRYTYPE_M                  GENMASK(10, 9)
+#define ANA_TABLES_MACACCESS_ENTRYTYPE_X(x)               (((x) & GENMASK(10, 9)) >> 9)
+#define ANA_TABLES_MACACCESS_DEST_IDX(x)                  (((x) << 3) & GENMASK(8, 3))
+#define ANA_TABLES_MACACCESS_DEST_IDX_M                   GENMASK(8, 3)
+#define ANA_TABLES_MACACCESS_DEST_IDX_X(x)                (((x) & GENMASK(8, 3)) >> 3)
+#define ANA_TABLES_MACACCESS_MAC_TABLE_CMD(x)             ((x) & GENMASK(2, 0))
+#define ANA_TABLES_MACACCESS_MAC_TABLE_CMD_M              GENMASK(2, 0)
+#define MACACCESS_CMD_IDLE                     0
+#define MACACCESS_CMD_LEARN                    1
+#define MACACCESS_CMD_FORGET                   2
+#define MACACCESS_CMD_AGE                      3
+#define MACACCESS_CMD_GET_NEXT                 4
+#define MACACCESS_CMD_INIT                     5
+#define MACACCESS_CMD_READ                     6
+#define MACACCESS_CMD_WRITE                    7
+
+#define ANA_TABLES_VLANACCESS_VLAN_PORT_MASK(x)           (((x) << 2) & GENMASK(13, 2))
+#define ANA_TABLES_VLANACCESS_VLAN_PORT_MASK_M            GENMASK(13, 2)
+#define ANA_TABLES_VLANACCESS_VLAN_PORT_MASK_X(x)         (((x) & GENMASK(13, 2)) >> 2)
+#define ANA_TABLES_VLANACCESS_VLAN_TBL_CMD(x)             ((x) & GENMASK(1, 0))
+#define ANA_TABLES_VLANACCESS_VLAN_TBL_CMD_M              GENMASK(1, 0)
+#define ANA_TABLES_VLANACCESS_CMD_IDLE                    0x0
+#define ANA_TABLES_VLANACCESS_CMD_WRITE                   0x2
+#define ANA_TABLES_VLANACCESS_CMD_INIT                    0x3
+
+#define ANA_TABLES_VLANTIDX_VLAN_SEC_FWD_ENA              BIT(17)
+#define ANA_TABLES_VLANTIDX_VLAN_FLOOD_DIS                BIT(16)
+#define ANA_TABLES_VLANTIDX_VLAN_PRIV_VLAN                BIT(15)
+#define ANA_TABLES_VLANTIDX_VLAN_LEARN_DISABLED           BIT(14)
+#define ANA_TABLES_VLANTIDX_VLAN_MIRROR                   BIT(13)
+#define ANA_TABLES_VLANTIDX_VLAN_SRC_CHK                  BIT(12)
+#define ANA_TABLES_VLANTIDX_V_INDEX(x)                    ((x) & GENMASK(11, 0))
+#define ANA_TABLES_VLANTIDX_V_INDEX_M                     GENMASK(11, 0)
+
+#define ANA_TABLES_ISDXACCESS_ISDX_PORT_MASK(x)           (((x) << 2) & GENMASK(8, 2))
+#define ANA_TABLES_ISDXACCESS_ISDX_PORT_MASK_M            GENMASK(8, 2)
+#define ANA_TABLES_ISDXACCESS_ISDX_PORT_MASK_X(x)         (((x) & GENMASK(8, 2)) >> 2)
+#define ANA_TABLES_ISDXACCESS_ISDX_TBL_CMD(x)             ((x) & GENMASK(1, 0))
+#define ANA_TABLES_ISDXACCESS_ISDX_TBL_CMD_M              GENMASK(1, 0)
+
+#define ANA_TABLES_ISDXTIDX_ISDX_SDLBI(x)                 (((x) << 21) & GENMASK(28, 21))
+#define ANA_TABLES_ISDXTIDX_ISDX_SDLBI_M                  GENMASK(28, 21)
+#define ANA_TABLES_ISDXTIDX_ISDX_SDLBI_X(x)               (((x) & GENMASK(28, 21)) >> 21)
+#define ANA_TABLES_ISDXTIDX_ISDX_MSTI(x)                  (((x) << 15) & GENMASK(20, 15))
+#define ANA_TABLES_ISDXTIDX_ISDX_MSTI_M                   GENMASK(20, 15)
+#define ANA_TABLES_ISDXTIDX_ISDX_MSTI_X(x)                (((x) & GENMASK(20, 15)) >> 15)
+#define ANA_TABLES_ISDXTIDX_ISDX_ES0_KEY_ENA              BIT(14)
+#define ANA_TABLES_ISDXTIDX_ISDX_FORCE_ENA                BIT(10)
+#define ANA_TABLES_ISDXTIDX_ISDX_INDEX(x)                 ((x) & GENMASK(7, 0))
+#define ANA_TABLES_ISDXTIDX_ISDX_INDEX_M                  GENMASK(7, 0)
+
+#define ANA_TABLES_ENTRYLIM_RSZ                           0x4
+
+#define ANA_TABLES_ENTRYLIM_ENTRYLIM(x)                   (((x) << 14) & GENMASK(17, 14))
+#define ANA_TABLES_ENTRYLIM_ENTRYLIM_M                    GENMASK(17, 14)
+#define ANA_TABLES_ENTRYLIM_ENTRYLIM_X(x)                 (((x) & GENMASK(17, 14)) >> 14)
+#define ANA_TABLES_ENTRYLIM_ENTRYSTAT(x)                  ((x) & GENMASK(13, 0))
+#define ANA_TABLES_ENTRYLIM_ENTRYSTAT_M                   GENMASK(13, 0)
+
+#define ANA_TABLES_STREAMACCESS_GEN_REC_SEQ_NUM(x)        (((x) << 4) & GENMASK(31, 4))
+#define ANA_TABLES_STREAMACCESS_GEN_REC_SEQ_NUM_M         GENMASK(31, 4)
+#define ANA_TABLES_STREAMACCESS_GEN_REC_SEQ_NUM_X(x)      (((x) & GENMASK(31, 4)) >> 4)
+#define ANA_TABLES_STREAMACCESS_SEQ_GEN_REC_ENA           BIT(3)
+#define ANA_TABLES_STREAMACCESS_GEN_REC_TYPE              BIT(2)
+#define ANA_TABLES_STREAMACCESS_STREAM_TBL_CMD(x)         ((x) & GENMASK(1, 0))
+#define ANA_TABLES_STREAMACCESS_STREAM_TBL_CMD_M          GENMASK(1, 0)
+
+#define ANA_TABLES_STREAMTIDX_SEQ_GEN_ERR_STATUS(x)       (((x) << 30) & GENMASK(31, 30))
+#define ANA_TABLES_STREAMTIDX_SEQ_GEN_ERR_STATUS_M        GENMASK(31, 30)
+#define ANA_TABLES_STREAMTIDX_SEQ_GEN_ERR_STATUS_X(x)     (((x) & GENMASK(31, 30)) >> 30)
+#define ANA_TABLES_STREAMTIDX_S_INDEX(x)                  (((x) << 16) & GENMASK(22, 16))
+#define ANA_TABLES_STREAMTIDX_S_INDEX_M                   GENMASK(22, 16)
+#define ANA_TABLES_STREAMTIDX_S_INDEX_X(x)                (((x) & GENMASK(22, 16)) >> 16)
+#define ANA_TABLES_STREAMTIDX_FORCE_SF_BEHAVIOUR          BIT(14)
+#define ANA_TABLES_STREAMTIDX_SEQ_HISTORY_LEN(x)          (((x) << 8) & GENMASK(13, 8))
+#define ANA_TABLES_STREAMTIDX_SEQ_HISTORY_LEN_M           GENMASK(13, 8)
+#define ANA_TABLES_STREAMTIDX_SEQ_HISTORY_LEN_X(x)        (((x) & GENMASK(13, 8)) >> 8)
+#define ANA_TABLES_STREAMTIDX_RESET_ON_ROGUE              BIT(7)
+#define ANA_TABLES_STREAMTIDX_REDTAG_POP                  BIT(6)
+#define ANA_TABLES_STREAMTIDX_STREAM_SPLIT                BIT(5)
+#define ANA_TABLES_STREAMTIDX_SEQ_SPACE_LOG2(x)           ((x) & GENMASK(4, 0))
+#define ANA_TABLES_STREAMTIDX_SEQ_SPACE_LOG2_M            GENMASK(4, 0)
+
+#define ANA_TABLES_SEQ_MASK_SPLIT_MASK(x)                 (((x) << 16) & GENMASK(22, 16))
+#define ANA_TABLES_SEQ_MASK_SPLIT_MASK_M                  GENMASK(22, 16)
+#define ANA_TABLES_SEQ_MASK_SPLIT_MASK_X(x)               (((x) & GENMASK(22, 16)) >> 16)
+#define ANA_TABLES_SEQ_MASK_INPUT_PORT_MASK(x)            ((x) & GENMASK(6, 0))
+#define ANA_TABLES_SEQ_MASK_INPUT_PORT_MASK_M             GENMASK(6, 0)
+
+#define ANA_TABLES_SFID_MASK_IGR_PORT_MASK(x)             (((x) << 1) & GENMASK(7, 1))
+#define ANA_TABLES_SFID_MASK_IGR_PORT_MASK_M              GENMASK(7, 1)
+#define ANA_TABLES_SFID_MASK_IGR_PORT_MASK_X(x)           (((x) & GENMASK(7, 1)) >> 1)
+#define ANA_TABLES_SFID_MASK_IGR_SRCPORT_MATCH_ENA        BIT(0)
+
+#define ANA_TABLES_SFIDACCESS_IGR_PRIO_MATCH_ENA          BIT(22)
+#define ANA_TABLES_SFIDACCESS_IGR_PRIO(x)                 (((x) << 19) & GENMASK(21, 19))
+#define ANA_TABLES_SFIDACCESS_IGR_PRIO_M                  GENMASK(21, 19)
+#define ANA_TABLES_SFIDACCESS_IGR_PRIO_X(x)               (((x) & GENMASK(21, 19)) >> 19)
+#define ANA_TABLES_SFIDACCESS_FORCE_BLOCK                 BIT(18)
+#define ANA_TABLES_SFIDACCESS_MAX_SDU_LEN(x)              (((x) << 2) & GENMASK(17, 2))
+#define ANA_TABLES_SFIDACCESS_MAX_SDU_LEN_M               GENMASK(17, 2)
+#define ANA_TABLES_SFIDACCESS_MAX_SDU_LEN_X(x)            (((x) & GENMASK(17, 2)) >> 2)
+#define ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(x)             ((x) & GENMASK(1, 0))
+#define ANA_TABLES_SFIDACCESS_SFID_TBL_CMD_M              GENMASK(1, 0)
+
+#define ANA_TABLES_SFIDTIDX_SGID_VALID                    BIT(26)
+#define ANA_TABLES_SFIDTIDX_SGID(x)                       (((x) << 18) & GENMASK(25, 18))
+#define ANA_TABLES_SFIDTIDX_SGID_M                        GENMASK(25, 18)
+#define ANA_TABLES_SFIDTIDX_SGID_X(x)                     (((x) & GENMASK(25, 18)) >> 18)
+#define ANA_TABLES_SFIDTIDX_POL_ENA                       BIT(17)
+#define ANA_TABLES_SFIDTIDX_POL_IDX(x)                    (((x) << 8) & GENMASK(16, 8))
+#define ANA_TABLES_SFIDTIDX_POL_IDX_M                     GENMASK(16, 8)
+#define ANA_TABLES_SFIDTIDX_POL_IDX_X(x)                  (((x) & GENMASK(16, 8)) >> 8)
+#define ANA_TABLES_SFIDTIDX_SFID_INDEX(x)                 ((x) & GENMASK(7, 0))
+#define ANA_TABLES_SFIDTIDX_SFID_INDEX_M                  GENMASK(7, 0)
+
+#define ANA_MSTI_STATE_RSZ                                0x4
+
+#define ANA_OAM_UPM_LM_CNT_RSZ                            0x4
+
+#define ANA_SG_ACCESS_CTRL_SGID(x)                        ((x) & GENMASK(7, 0))
+#define ANA_SG_ACCESS_CTRL_SGID_M                         GENMASK(7, 0)
+#define ANA_SG_ACCESS_CTRL_CONFIG_CHANGE                  BIT(28)
+
+#define ANA_SG_CONFIG_REG_3_BASE_TIME_SEC_MSB(x)          ((x) & GENMASK(15, 0))
+#define ANA_SG_CONFIG_REG_3_BASE_TIME_SEC_MSB_M           GENMASK(15, 0)
+#define ANA_SG_CONFIG_REG_3_LIST_LENGTH(x)                (((x) << 16) & GENMASK(18, 16))
+#define ANA_SG_CONFIG_REG_3_LIST_LENGTH_M                 GENMASK(18, 16)
+#define ANA_SG_CONFIG_REG_3_LIST_LENGTH_X(x)              (((x) & GENMASK(18, 16)) >> 16)
+#define ANA_SG_CONFIG_REG_3_GATE_ENABLE                   BIT(20)
+#define ANA_SG_CONFIG_REG_3_INIT_IPS(x)                   (((x) << 24) & GENMASK(27, 24))
+#define ANA_SG_CONFIG_REG_3_INIT_IPS_M                    GENMASK(27, 24)
+#define ANA_SG_CONFIG_REG_3_INIT_IPS_X(x)                 (((x) & GENMASK(27, 24)) >> 24)
+#define ANA_SG_CONFIG_REG_3_INIT_GATE_STATE               BIT(28)
+
+#define ANA_SG_GCL_GS_CONFIG_RSZ                          0x4
+
+#define ANA_SG_GCL_GS_CONFIG_IPS(x)                       ((x) & GENMASK(3, 0))
+#define ANA_SG_GCL_GS_CONFIG_IPS_M                        GENMASK(3, 0)
+#define ANA_SG_GCL_GS_CONFIG_GATE_STATE                   BIT(4)
+
+#define ANA_SG_GCL_TI_CONFIG_RSZ                          0x4
+
+#define ANA_SG_STATUS_REG_3_CFG_CHG_TIME_SEC_MSB(x)       ((x) & GENMASK(15, 0))
+#define ANA_SG_STATUS_REG_3_CFG_CHG_TIME_SEC_MSB_M        GENMASK(15, 0)
+#define ANA_SG_STATUS_REG_3_GATE_STATE                    BIT(16)
+#define ANA_SG_STATUS_REG_3_IPS(x)                        (((x) << 20) & GENMASK(23, 20))
+#define ANA_SG_STATUS_REG_3_IPS_M                         GENMASK(23, 20)
+#define ANA_SG_STATUS_REG_3_IPS_X(x)                      (((x) & GENMASK(23, 20)) >> 20)
+#define ANA_SG_STATUS_REG_3_CONFIG_PENDING                BIT(24)
+
+#define ANA_PORT_VLAN_CFG_GSZ                             0x100
+
+#define ANA_PORT_VLAN_CFG_VLAN_VID_AS_ISDX                BIT(21)
+#define ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA                  BIT(20)
+#define ANA_PORT_VLAN_CFG_VLAN_POP_CNT(x)                 (((x) << 18) & GENMASK(19, 18))
+#define ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M                  GENMASK(19, 18)
+#define ANA_PORT_VLAN_CFG_VLAN_POP_CNT_X(x)               (((x) & GENMASK(19, 18)) >> 18)
+#define ANA_PORT_VLAN_CFG_VLAN_INNER_TAG_ENA              BIT(17)
+#define ANA_PORT_VLAN_CFG_VLAN_TAG_TYPE                   BIT(16)
+#define ANA_PORT_VLAN_CFG_VLAN_DEI                        BIT(15)
+#define ANA_PORT_VLAN_CFG_VLAN_PCP(x)                     (((x) << 12) & GENMASK(14, 12))
+#define ANA_PORT_VLAN_CFG_VLAN_PCP_M                      GENMASK(14, 12)
+#define ANA_PORT_VLAN_CFG_VLAN_PCP_X(x)                   (((x) & GENMASK(14, 12)) >> 12)
+#define ANA_PORT_VLAN_CFG_VLAN_VID(x)                     ((x) & GENMASK(11, 0))
+#define ANA_PORT_VLAN_CFG_VLAN_VID_M                      GENMASK(11, 0)
+
+#define ANA_PORT_DROP_CFG_GSZ                             0x100
+
+#define ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA               BIT(6)
+#define ANA_PORT_DROP_CFG_DROP_S_TAGGED_ENA               BIT(5)
+#define ANA_PORT_DROP_CFG_DROP_C_TAGGED_ENA               BIT(4)
+#define ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA          BIT(3)
+#define ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA          BIT(2)
+#define ANA_PORT_DROP_CFG_DROP_NULL_MAC_ENA               BIT(1)
+#define ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA                BIT(0)
+
+#define ANA_PORT_QOS_CFG_GSZ                              0x100
+
+#define ANA_PORT_QOS_CFG_DP_DEFAULT_VAL                   BIT(8)
+#define ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL(x)               (((x) << 5) & GENMASK(7, 5))
+#define ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_M                GENMASK(7, 5)
+#define ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_X(x)             (((x) & GENMASK(7, 5)) >> 5)
+#define ANA_PORT_QOS_CFG_QOS_DSCP_ENA                     BIT(4)
+#define ANA_PORT_QOS_CFG_QOS_PCP_ENA                      BIT(3)
+#define ANA_PORT_QOS_CFG_DSCP_TRANSLATE_ENA               BIT(2)
+#define ANA_PORT_QOS_CFG_DSCP_REWR_CFG(x)                 ((x) & GENMASK(1, 0))
+#define ANA_PORT_QOS_CFG_DSCP_REWR_CFG_M                  GENMASK(1, 0)
+
+#define ANA_PORT_VCAP_CFG_GSZ                             0x100
+
+#define ANA_PORT_VCAP_CFG_S1_ENA                          BIT(14)
+#define ANA_PORT_VCAP_CFG_S1_DMAC_DIP_ENA(x)              (((x) << 11) & GENMASK(13, 11))
+#define ANA_PORT_VCAP_CFG_S1_DMAC_DIP_ENA_M               GENMASK(13, 11)
+#define ANA_PORT_VCAP_CFG_S1_DMAC_DIP_ENA_X(x)            (((x) & GENMASK(13, 11)) >> 11)
+#define ANA_PORT_VCAP_CFG_S1_VLAN_INNER_TAG_ENA(x)        (((x) << 8) & GENMASK(10, 8))
+#define ANA_PORT_VCAP_CFG_S1_VLAN_INNER_TAG_ENA_M         GENMASK(10, 8)
+#define ANA_PORT_VCAP_CFG_S1_VLAN_INNER_TAG_ENA_X(x)      (((x) & GENMASK(10, 8)) >> 8)
+#define ANA_PORT_VCAP_CFG_PAG_VAL(x)                      ((x) & GENMASK(7, 0))
+#define ANA_PORT_VCAP_CFG_PAG_VAL_M                       GENMASK(7, 0)
+
+#define ANA_PORT_VCAP_S1_KEY_CFG_GSZ                      0x100
+#define ANA_PORT_VCAP_S1_KEY_CFG_RSZ                      0x4
+
+#define ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_IP6_CFG(x)        (((x) << 4) & GENMASK(6, 4))
+#define ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_IP6_CFG_M         GENMASK(6, 4)
+#define ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_IP6_CFG_X(x)      (((x) & GENMASK(6, 4)) >> 4)
+#define ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_IP4_CFG(x)        (((x) << 2) & GENMASK(3, 2))
+#define ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_IP4_CFG_M         GENMASK(3, 2)
+#define ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_IP4_CFG_X(x)      (((x) & GENMASK(3, 2)) >> 2)
+#define ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_OTHER_CFG(x)      ((x) & GENMASK(1, 0))
+#define ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_OTHER_CFG_M       GENMASK(1, 0)
+
+#define ANA_PORT_VCAP_S2_CFG_GSZ                          0x100
+
+#define ANA_PORT_VCAP_S2_CFG_S2_UDP_PAYLOAD_ENA(x)        (((x) << 17) & GENMASK(18, 17))
+#define ANA_PORT_VCAP_S2_CFG_S2_UDP_PAYLOAD_ENA_M         GENMASK(18, 17)
+#define ANA_PORT_VCAP_S2_CFG_S2_UDP_PAYLOAD_ENA_X(x)      (((x) & GENMASK(18, 17)) >> 17)
+#define ANA_PORT_VCAP_S2_CFG_S2_ETYPE_PAYLOAD_ENA(x)      (((x) << 15) & GENMASK(16, 15))
+#define ANA_PORT_VCAP_S2_CFG_S2_ETYPE_PAYLOAD_ENA_M       GENMASK(16, 15)
+#define ANA_PORT_VCAP_S2_CFG_S2_ETYPE_PAYLOAD_ENA_X(x)    (((x) & GENMASK(16, 15)) >> 15)
+#define ANA_PORT_VCAP_S2_CFG_S2_ENA                       BIT(14)
+#define ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS(x)               (((x) << 12) & GENMASK(13, 12))
+#define ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS_M                GENMASK(13, 12)
+#define ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS_X(x)             (((x) & GENMASK(13, 12)) >> 12)
+#define ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS(x)                (((x) << 10) & GENMASK(11, 10))
+#define ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS_M                 GENMASK(11, 10)
+#define ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS_X(x)              (((x) & GENMASK(11, 10)) >> 10)
+#define ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS(x)          (((x) << 8) & GENMASK(9, 8))
+#define ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS_M           GENMASK(9, 8)
+#define ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS_X(x)        (((x) & GENMASK(9, 8)) >> 8)
+#define ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS(x)           (((x) << 6) & GENMASK(7, 6))
+#define ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS_M            GENMASK(7, 6)
+#define ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS_X(x)         (((x) & GENMASK(7, 6)) >> 6)
+#define ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG(x)                (((x) << 2) & GENMASK(5, 2))
+#define ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG_M                 GENMASK(5, 2)
+#define ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG_X(x)              (((x) & GENMASK(5, 2)) >> 2)
+#define ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS(x)                ((x) & GENMASK(1, 0))
+#define ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS_M                 GENMASK(1, 0)
+
+#define ANA_PORT_PCP_DEI_MAP_GSZ                          0x100
+#define ANA_PORT_PCP_DEI_MAP_RSZ                          0x4
+
+#define ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL               BIT(3)
+#define ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL(x)           ((x) & GENMASK(2, 0))
+#define ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL_M            GENMASK(2, 0)
+
+#define ANA_PORT_CPU_FWD_CFG_GSZ                          0x100
+
+#define ANA_PORT_CPU_FWD_CFG_CPU_VRAP_REDIR_ENA           BIT(7)
+#define ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA            BIT(6)
+#define ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA           BIT(5)
+#define ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA       BIT(4)
+#define ANA_PORT_CPU_FWD_CFG_CPU_SRC_COPY_ENA             BIT(3)
+#define ANA_PORT_CPU_FWD_CFG_CPU_ALLBRIDGE_DROP_ENA       BIT(2)
+#define ANA_PORT_CPU_FWD_CFG_CPU_ALLBRIDGE_REDIR_ENA      BIT(1)
+#define ANA_PORT_CPU_FWD_CFG_CPU_OAM_ENA                  BIT(0)
+
+#define ANA_PORT_CPU_FWD_BPDU_CFG_GSZ                     0x100
+
+#define ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_DROP_ENA(x)        (((x) << 16) & GENMASK(31, 16))
+#define ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_DROP_ENA_M         GENMASK(31, 16)
+#define ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_DROP_ENA_X(x)      (((x) & GENMASK(31, 16)) >> 16)
+#define ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(x)       ((x) & GENMASK(15, 0))
+#define ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA_M        GENMASK(15, 0)
+
+#define ANA_PORT_CPU_FWD_GARP_CFG_GSZ                     0x100
+
+#define ANA_PORT_CPU_FWD_GARP_CFG_GARP_DROP_ENA(x)        (((x) << 16) & GENMASK(31, 16))
+#define ANA_PORT_CPU_FWD_GARP_CFG_GARP_DROP_ENA_M         GENMASK(31, 16)
+#define ANA_PORT_CPU_FWD_GARP_CFG_GARP_DROP_ENA_X(x)      (((x) & GENMASK(31, 16)) >> 16)
+#define ANA_PORT_CPU_FWD_GARP_CFG_GARP_REDIR_ENA(x)       ((x) & GENMASK(15, 0))
+#define ANA_PORT_CPU_FWD_GARP_CFG_GARP_REDIR_ENA_M        GENMASK(15, 0)
+
+#define ANA_PORT_CPU_FWD_CCM_CFG_GSZ                      0x100
+
+#define ANA_PORT_CPU_FWD_CCM_CFG_CCM_DROP_ENA(x)          (((x) << 16) & GENMASK(31, 16))
+#define ANA_PORT_CPU_FWD_CCM_CFG_CCM_DROP_ENA_M           GENMASK(31, 16)
+#define ANA_PORT_CPU_FWD_CCM_CFG_CCM_DROP_ENA_X(x)        (((x) & GENMASK(31, 16)) >> 16)
+#define ANA_PORT_CPU_FWD_CCM_CFG_CCM_REDIR_ENA(x)         ((x) & GENMASK(15, 0))
+#define ANA_PORT_CPU_FWD_CCM_CFG_CCM_REDIR_ENA_M          GENMASK(15, 0)
+
+#define ANA_PORT_PORT_CFG_GSZ                             0x100
+
+#define ANA_PORT_PORT_CFG_SRC_MIRROR_ENA                  BIT(15)
+#define ANA_PORT_PORT_CFG_LIMIT_DROP                      BIT(14)
+#define ANA_PORT_PORT_CFG_LIMIT_CPU                       BIT(13)
+#define ANA_PORT_PORT_CFG_LOCKED_PORTMOVE_DROP            BIT(12)
+#define ANA_PORT_PORT_CFG_LOCKED_PORTMOVE_CPU             BIT(11)
+#define ANA_PORT_PORT_CFG_LEARNDROP                       BIT(10)
+#define ANA_PORT_PORT_CFG_LEARNCPU                        BIT(9)
+#define ANA_PORT_PORT_CFG_LEARNAUTO                       BIT(8)
+#define ANA_PORT_PORT_CFG_LEARN_ENA                       BIT(7)
+#define ANA_PORT_PORT_CFG_RECV_ENA                        BIT(6)
+#define ANA_PORT_PORT_CFG_PORTID_VAL(x)                   (((x) << 2) & GENMASK(5, 2))
+#define ANA_PORT_PORT_CFG_PORTID_VAL_M                    GENMASK(5, 2)
+#define ANA_PORT_PORT_CFG_PORTID_VAL_X(x)                 (((x) & GENMASK(5, 2)) >> 2)
+#define ANA_PORT_PORT_CFG_USE_B_DOM_TBL                   BIT(1)
+#define ANA_PORT_PORT_CFG_LSR_MODE                        BIT(0)
+
+#define ANA_PORT_POL_CFG_GSZ                              0x100
+
+#define ANA_PORT_POL_CFG_POL_CPU_REDIR_8021               BIT(19)
+#define ANA_PORT_POL_CFG_POL_CPU_REDIR_IP                 BIT(18)
+#define ANA_PORT_POL_CFG_PORT_POL_ENA                     BIT(17)
+#define ANA_PORT_POL_CFG_QUEUE_POL_ENA(x)                 (((x) << 9) & GENMASK(16, 9))
+#define ANA_PORT_POL_CFG_QUEUE_POL_ENA_M                  GENMASK(16, 9)
+#define ANA_PORT_POL_CFG_QUEUE_POL_ENA_X(x)               (((x) & GENMASK(16, 9)) >> 9)
+#define ANA_PORT_POL_CFG_POL_ORDER(x)                     ((x) & GENMASK(8, 0))
+#define ANA_PORT_POL_CFG_POL_ORDER_M                      GENMASK(8, 0)
+
+#define ANA_PORT_PTP_CFG_GSZ                              0x100
+
+#define ANA_PORT_PTP_CFG_PTP_BACKPLANE_MODE               BIT(0)
+
+#define ANA_PORT_PTP_DLY1_CFG_GSZ                         0x100
+
+#define ANA_PORT_PTP_DLY2_CFG_GSZ                         0x100
+
+#define ANA_PORT_SFID_CFG_GSZ                             0x100
+#define ANA_PORT_SFID_CFG_RSZ                             0x4
+
+#define ANA_PORT_SFID_CFG_SFID_VALID                      BIT(8)
+#define ANA_PORT_SFID_CFG_SFID(x)                         ((x) & GENMASK(7, 0))
+#define ANA_PORT_SFID_CFG_SFID_M                          GENMASK(7, 0)
+
+#define ANA_PFC_PFC_CFG_GSZ                               0x40
+
+#define ANA_PFC_PFC_CFG_RX_PFC_ENA(x)                     (((x) << 2) & GENMASK(9, 2))
+#define ANA_PFC_PFC_CFG_RX_PFC_ENA_M                      GENMASK(9, 2)
+#define ANA_PFC_PFC_CFG_RX_PFC_ENA_X(x)                   (((x) & GENMASK(9, 2)) >> 2)
+#define ANA_PFC_PFC_CFG_FC_LINK_SPEED(x)                  ((x) & GENMASK(1, 0))
+#define ANA_PFC_PFC_CFG_FC_LINK_SPEED_M                   GENMASK(1, 0)
+
+#define ANA_PFC_PFC_TIMER_GSZ                             0x40
+#define ANA_PFC_PFC_TIMER_RSZ                             0x4
+
+#define ANA_IPT_OAM_MEP_CFG_GSZ                           0x8
+
+#define ANA_IPT_OAM_MEP_CFG_MEP_IDX_P(x)                  (((x) << 6) & GENMASK(10, 6))
+#define ANA_IPT_OAM_MEP_CFG_MEP_IDX_P_M                   GENMASK(10, 6)
+#define ANA_IPT_OAM_MEP_CFG_MEP_IDX_P_X(x)                (((x) & GENMASK(10, 6)) >> 6)
+#define ANA_IPT_OAM_MEP_CFG_MEP_IDX(x)                    (((x) << 1) & GENMASK(5, 1))
+#define ANA_IPT_OAM_MEP_CFG_MEP_IDX_M                     GENMASK(5, 1)
+#define ANA_IPT_OAM_MEP_CFG_MEP_IDX_X(x)                  (((x) & GENMASK(5, 1)) >> 1)
+#define ANA_IPT_OAM_MEP_CFG_MEP_IDX_ENA                   BIT(0)
+
+#define ANA_IPT_IPT_GSZ                                   0x8
+
+#define ANA_IPT_IPT_IPT_CFG(x)                            (((x) << 15) & GENMASK(16, 15))
+#define ANA_IPT_IPT_IPT_CFG_M                             GENMASK(16, 15)
+#define ANA_IPT_IPT_IPT_CFG_X(x)                          (((x) & GENMASK(16, 15)) >> 15)
+#define ANA_IPT_IPT_ISDX_P(x)                             (((x) << 7) & GENMASK(14, 7))
+#define ANA_IPT_IPT_ISDX_P_M                              GENMASK(14, 7)
+#define ANA_IPT_IPT_ISDX_P_X(x)                           (((x) & GENMASK(14, 7)) >> 7)
+#define ANA_IPT_IPT_PPT_IDX(x)                            ((x) & GENMASK(6, 0))
+#define ANA_IPT_IPT_PPT_IDX_M                             GENMASK(6, 0)
+
+#define ANA_PPT_PPT_RSZ                                   0x4
+
+#define ANA_FID_MAP_FID_MAP_RSZ                           0x4
+
+#define ANA_FID_MAP_FID_MAP_FID_C_VAL(x)                  (((x) << 6) & GENMASK(11, 6))
+#define ANA_FID_MAP_FID_MAP_FID_C_VAL_M                   GENMASK(11, 6)
+#define ANA_FID_MAP_FID_MAP_FID_C_VAL_X(x)                (((x) & GENMASK(11, 6)) >> 6)
+#define ANA_FID_MAP_FID_MAP_FID_B_VAL(x)                  ((x) & GENMASK(5, 0))
+#define ANA_FID_MAP_FID_MAP_FID_B_VAL_M                   GENMASK(5, 0)
+
+#define ANA_AGGR_CFG_AC_RND_ENA                           BIT(7)
+#define ANA_AGGR_CFG_AC_DMAC_ENA                          BIT(6)
+#define ANA_AGGR_CFG_AC_SMAC_ENA                          BIT(5)
+#define ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA                  BIT(4)
+#define ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA                    BIT(3)
+#define ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA                    BIT(2)
+#define ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA                    BIT(1)
+#define ANA_AGGR_CFG_AC_ISDX_ENA                          BIT(0)
+
+#define ANA_CPUQ_CFG_CPUQ_MLD(x)                          (((x) << 27) & GENMASK(29, 27))
+#define ANA_CPUQ_CFG_CPUQ_MLD_M                           GENMASK(29, 27)
+#define ANA_CPUQ_CFG_CPUQ_MLD_X(x)                        (((x) & GENMASK(29, 27)) >> 27)
+#define ANA_CPUQ_CFG_CPUQ_IGMP(x)                         (((x) << 24) & GENMASK(26, 24))
+#define ANA_CPUQ_CFG_CPUQ_IGMP_M                          GENMASK(26, 24)
+#define ANA_CPUQ_CFG_CPUQ_IGMP_X(x)                       (((x) & GENMASK(26, 24)) >> 24)
+#define ANA_CPUQ_CFG_CPUQ_IPMC_CTRL(x)                    (((x) << 21) & GENMASK(23, 21))
+#define ANA_CPUQ_CFG_CPUQ_IPMC_CTRL_M                     GENMASK(23, 21)
+#define ANA_CPUQ_CFG_CPUQ_IPMC_CTRL_X(x)                  (((x) & GENMASK(23, 21)) >> 21)
+#define ANA_CPUQ_CFG_CPUQ_ALLBRIDGE(x)                    (((x) << 18) & GENMASK(20, 18))
+#define ANA_CPUQ_CFG_CPUQ_ALLBRIDGE_M                     GENMASK(20, 18)
+#define ANA_CPUQ_CFG_CPUQ_ALLBRIDGE_X(x)                  (((x) & GENMASK(20, 18)) >> 18)
+#define ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE(x)              (((x) << 15) & GENMASK(17, 15))
+#define ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE_M               GENMASK(17, 15)
+#define ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE_X(x)            (((x) & GENMASK(17, 15)) >> 15)
+#define ANA_CPUQ_CFG_CPUQ_SRC_COPY(x)                     (((x) << 12) & GENMASK(14, 12))
+#define ANA_CPUQ_CFG_CPUQ_SRC_COPY_M                      GENMASK(14, 12)
+#define ANA_CPUQ_CFG_CPUQ_SRC_COPY_X(x)                   (((x) & GENMASK(14, 12)) >> 12)
+#define ANA_CPUQ_CFG_CPUQ_MAC_COPY(x)                     (((x) << 9) & GENMASK(11, 9))
+#define ANA_CPUQ_CFG_CPUQ_MAC_COPY_M                      GENMASK(11, 9)
+#define ANA_CPUQ_CFG_CPUQ_MAC_COPY_X(x)                   (((x) & GENMASK(11, 9)) >> 9)
+#define ANA_CPUQ_CFG_CPUQ_LRN(x)                          (((x) << 6) & GENMASK(8, 6))
+#define ANA_CPUQ_CFG_CPUQ_LRN_M                           GENMASK(8, 6)
+#define ANA_CPUQ_CFG_CPUQ_LRN_X(x)                        (((x) & GENMASK(8, 6)) >> 6)
+#define ANA_CPUQ_CFG_CPUQ_MIRROR(x)                       (((x) << 3) & GENMASK(5, 3))
+#define ANA_CPUQ_CFG_CPUQ_MIRROR_M                        GENMASK(5, 3)
+#define ANA_CPUQ_CFG_CPUQ_MIRROR_X(x)                     (((x) & GENMASK(5, 3)) >> 3)
+#define ANA_CPUQ_CFG_CPUQ_SFLOW(x)                        ((x) & GENMASK(2, 0))
+#define ANA_CPUQ_CFG_CPUQ_SFLOW_M                         GENMASK(2, 0)
+
+#define ANA_CPUQ_8021_CFG_RSZ                             0x4
+
+#define ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL(x)                (((x) << 6) & GENMASK(8, 6))
+#define ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL_M                 GENMASK(8, 6)
+#define ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL_X(x)              (((x) & GENMASK(8, 6)) >> 6)
+#define ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL(x)                (((x) << 3) & GENMASK(5, 3))
+#define ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL_M                 GENMASK(5, 3)
+#define ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL_X(x)              (((x) & GENMASK(5, 3)) >> 3)
+#define ANA_CPUQ_8021_CFG_CPUQ_CCM_VAL(x)                 ((x) & GENMASK(2, 0))
+#define ANA_CPUQ_8021_CFG_CPUQ_CCM_VAL_M                  GENMASK(2, 0)
+
+#define ANA_DSCP_CFG_RSZ                                  0x4
+
+#define ANA_DSCP_CFG_DP_DSCP_VAL                          BIT(11)
+#define ANA_DSCP_CFG_QOS_DSCP_VAL(x)                      (((x) << 8) & GENMASK(10, 8))
+#define ANA_DSCP_CFG_QOS_DSCP_VAL_M                       GENMASK(10, 8)
+#define ANA_DSCP_CFG_QOS_DSCP_VAL_X(x)                    (((x) & GENMASK(10, 8)) >> 8)
+#define ANA_DSCP_CFG_DSCP_TRANSLATE_VAL(x)                (((x) << 2) & GENMASK(7, 2))
+#define ANA_DSCP_CFG_DSCP_TRANSLATE_VAL_M                 GENMASK(7, 2)
+#define ANA_DSCP_CFG_DSCP_TRANSLATE_VAL_X(x)              (((x) & GENMASK(7, 2)) >> 2)
+#define ANA_DSCP_CFG_DSCP_TRUST_ENA                       BIT(1)
+#define ANA_DSCP_CFG_DSCP_REWR_ENA                        BIT(0)
+
+#define ANA_DSCP_REWR_CFG_RSZ                             0x4
+
+#define ANA_VCAP_RNG_TYPE_CFG_RSZ                         0x4
+
+#define ANA_VCAP_RNG_VAL_CFG_RSZ                          0x4
+
+#define ANA_VCAP_RNG_VAL_CFG_VCAP_RNG_MIN_VAL(x)          (((x) << 16) & GENMASK(31, 16))
+#define ANA_VCAP_RNG_VAL_CFG_VCAP_RNG_MIN_VAL_M           GENMASK(31, 16)
+#define ANA_VCAP_RNG_VAL_CFG_VCAP_RNG_MIN_VAL_X(x)        (((x) & GENMASK(31, 16)) >> 16)
+#define ANA_VCAP_RNG_VAL_CFG_VCAP_RNG_MAX_VAL(x)          ((x) & GENMASK(15, 0))
+#define ANA_VCAP_RNG_VAL_CFG_VCAP_RNG_MAX_VAL_M           GENMASK(15, 0)
+
+#define ANA_VRAP_CFG_VRAP_VLAN_AWARE_ENA                  BIT(12)
+#define ANA_VRAP_CFG_VRAP_VID(x)                          ((x) & GENMASK(11, 0))
+#define ANA_VRAP_CFG_VRAP_VID_M                           GENMASK(11, 0)
+
+#define ANA_DISCARD_CFG_DROP_TAGGING_ISDX0                BIT(3)
+#define ANA_DISCARD_CFG_DROP_CTRLPROT_ISDX0               BIT(2)
+#define ANA_DISCARD_CFG_DROP_TAGGING_S2_ENA               BIT(1)
+#define ANA_DISCARD_CFG_DROP_CTRLPROT_S2_ENA              BIT(0)
+
+#define ANA_FID_CFG_VID_MC_ENA                            BIT(0)
+
+#define ANA_POL_PIR_CFG_GSZ                               0x20
+
+#define ANA_POL_PIR_CFG_PIR_RATE(x)                       (((x) << 6) & GENMASK(20, 6))
+#define ANA_POL_PIR_CFG_PIR_RATE_M                        GENMASK(20, 6)
+#define ANA_POL_PIR_CFG_PIR_RATE_X(x)                     (((x) & GENMASK(20, 6)) >> 6)
+#define ANA_POL_PIR_CFG_PIR_BURST(x)                      ((x) & GENMASK(5, 0))
+#define ANA_POL_PIR_CFG_PIR_BURST_M                       GENMASK(5, 0)
+
+#define ANA_POL_CIR_CFG_GSZ                               0x20
+
+#define ANA_POL_CIR_CFG_CIR_RATE(x)                       (((x) << 6) & GENMASK(20, 6))
+#define ANA_POL_CIR_CFG_CIR_RATE_M                        GENMASK(20, 6)
+#define ANA_POL_CIR_CFG_CIR_RATE_X(x)                     (((x) & GENMASK(20, 6)) >> 6)
+#define ANA_POL_CIR_CFG_CIR_BURST(x)                      ((x) & GENMASK(5, 0))
+#define ANA_POL_CIR_CFG_CIR_BURST_M                       GENMASK(5, 0)
+
+#define ANA_POL_MODE_CFG_GSZ                              0x20
+
+#define ANA_POL_MODE_CFG_IPG_SIZE(x)                      (((x) << 5) & GENMASK(9, 5))
+#define ANA_POL_MODE_CFG_IPG_SIZE_M                       GENMASK(9, 5)
+#define ANA_POL_MODE_CFG_IPG_SIZE_X(x)                    (((x) & GENMASK(9, 5)) >> 5)
+#define ANA_POL_MODE_CFG_FRM_MODE(x)                      (((x) << 3) & GENMASK(4, 3))
+#define ANA_POL_MODE_CFG_FRM_MODE_M                       GENMASK(4, 3)
+#define ANA_POL_MODE_CFG_FRM_MODE_X(x)                    (((x) & GENMASK(4, 3)) >> 3)
+#define ANA_POL_MODE_CFG_DLB_COUPLED                      BIT(2)
+#define ANA_POL_MODE_CFG_CIR_ENA                          BIT(1)
+#define ANA_POL_MODE_CFG_OVERSHOOT_ENA                    BIT(0)
+
+#define ANA_POL_PIR_STATE_GSZ                             0x20
+
+#define ANA_POL_CIR_STATE_GSZ                             0x20
+
+#define ANA_POL_STATE_GSZ                                 0x20
+
+#define ANA_POL_FLOWC_RSZ                                 0x4
+
+#define ANA_POL_FLOWC_POL_FLOWC                           BIT(0)
+
+#define ANA_POL_HYST_POL_FC_HYST(x)                       (((x) << 4) & GENMASK(9, 4))
+#define ANA_POL_HYST_POL_FC_HYST_M                        GENMASK(9, 4)
+#define ANA_POL_HYST_POL_FC_HYST_X(x)                     (((x) & GENMASK(9, 4)) >> 4)
+#define ANA_POL_HYST_POL_STOP_HYST(x)                     ((x) & GENMASK(3, 0))
+#define ANA_POL_HYST_POL_STOP_HYST_M                      GENMASK(3, 0)
+
+#define ANA_POL_MISC_CFG_POL_CLOSE_ALL                    BIT(1)
+#define ANA_POL_MISC_CFG_POL_LEAK_DIS                     BIT(0)
+
+#endif
diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c
new file mode 100644 (file)
index 0000000..18df7d9
--- /dev/null
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+#include <linux/skbuff.h>
+
+#include "ocelot.h"
+
+static int ocelot_parse_ifh(u32 *ifh, struct frame_info *info)
+{
+       int i;
+       u8 llen, wlen;
+
+       /* The IFH is in network order, switch to CPU order */
+       for (i = 0; i < IFH_LEN; i++)
+               ifh[i] = ntohl((__force __be32)ifh[i]);
+
+       wlen = (ifh[1] >> 7) & 0xff;
+       llen = (ifh[1] >> 15) & 0x3f;
+       info->len = OCELOT_BUFFER_CELL_SZ * wlen + llen - 80;
+
+       info->port = (ifh[2] & GENMASK(14, 11)) >> 11;
+
+       info->cpuq = (ifh[3] & GENMASK(27, 20)) >> 20;
+       info->tag_type = (ifh[3] & GENMASK(16, 16)) >> 16;
+       info->vid = ifh[3] & GENMASK(11, 0);
+
+       return 0;
+}
+
+static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh,
+                               u32 *rval)
+{
+       u32 val;
+       u32 bytes_valid;
+
+       val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+       if (val == XTR_NOT_READY) {
+               if (ifh)
+                       return -EIO;
+
+               do {
+                       val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+               } while (val == XTR_NOT_READY);
+       }
+
+       switch (val) {
+       case XTR_ABORT:
+               return -EIO;
+       case XTR_EOF_0:
+       case XTR_EOF_1:
+       case XTR_EOF_2:
+       case XTR_EOF_3:
+       case XTR_PRUNED:
+               bytes_valid = XTR_VALID_BYTES(val);
+               val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+               if (val == XTR_ESCAPE)
+                       *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+               else
+                       *rval = val;
+
+               return bytes_valid;
+       case XTR_ESCAPE:
+               *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+
+               return 4;
+       default:
+               *rval = val;
+
+               return 4;
+       }
+}
+
+static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
+{
+       struct ocelot *ocelot = arg;
+       int i = 0, grp = 0;
+       int err = 0;
+
+       if (!(ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)))
+               return IRQ_NONE;
+
+       do {
+               struct sk_buff *skb;
+               struct net_device *dev;
+               u32 *buf;
+               int sz, len;
+               u32 ifh[4];
+               u32 val;
+               struct frame_info info;
+
+               for (i = 0; i < IFH_LEN; i++) {
+                       err = ocelot_rx_frame_word(ocelot, grp, true, &ifh[i]);
+                       if (err != 4)
+                               break;
+               }
+
+               if (err != 4)
+                       break;
+
+               ocelot_parse_ifh(ifh, &info);
+
+               dev = ocelot->ports[info.port]->dev;
+
+               skb = netdev_alloc_skb(dev, info.len);
+
+               if (unlikely(!skb)) {
+                       netdev_err(dev, "Unable to allocate sk_buff\n");
+                       err = -ENOMEM;
+                       break;
+               }
+               buf = (u32 *)skb_put(skb, info.len);
+
+               len = 0;
+               do {
+                       sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
+                       *buf++ = val;
+                       len += sz;
+               } while ((sz == 4) && (len < info.len));
+
+               if (sz < 0) {
+                       err = sz;
+                       break;
+               }
+
+               /* Everything we see on an interface that is in the HW bridge
+                * has already been forwarded.
+                */
+               if (ocelot->bridge_mask & BIT(info.port))
+                       skb->offload_fwd_mark = 1;
+
+               skb->protocol = eth_type_trans(skb, dev);
+               netif_rx(skb);
+               dev->stats.rx_bytes += len;
+               dev->stats.rx_packets++;
+       } while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp));
+
+       if (err)
+               while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp))
+                       ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+
+       return IRQ_HANDLED;
+}
+
+static const struct of_device_id mscc_ocelot_match[] = {
+       { .compatible = "mscc,vsc7514-switch" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mscc_ocelot_match);
+
+static int mscc_ocelot_probe(struct platform_device *pdev)
+{
+       int err, irq;
+       unsigned int i;
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *ports, *portnp;
+       struct ocelot *ocelot;
+       u32 val;
+
+       struct {
+               enum ocelot_target id;
+               char *name;
+       } res[] = {
+               { SYS, "sys" },
+               { REW, "rew" },
+               { QSYS, "qsys" },
+               { ANA, "ana" },
+               { QS, "qs" },
+               { HSIO, "hsio" },
+       };
+
+       if (!np && !pdev->dev.platform_data)
+               return -ENODEV;
+
+       ocelot = devm_kzalloc(&pdev->dev, sizeof(*ocelot), GFP_KERNEL);
+       if (!ocelot)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ocelot);
+       ocelot->dev = &pdev->dev;
+
+       for (i = 0; i < ARRAY_SIZE(res); i++) {
+               struct regmap *target;
+
+               target = ocelot_io_platform_init(ocelot, pdev, res[i].name);
+               if (IS_ERR(target))
+                       return PTR_ERR(target);
+
+               ocelot->targets[res[i].id] = target;
+       }
+
+       err = ocelot_chip_init(ocelot);
+       if (err)
+               return err;
+
+       irq = platform_get_irq_byname(pdev, "xtr");
+       if (irq < 0)
+               return -ENODEV;
+
+       err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+                                       ocelot_xtr_irq_handler, IRQF_ONESHOT,
+                                       "frame extraction", ocelot);
+       if (err)
+               return err;
+
+       regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1);
+       regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1);
+
+       do {
+               msleep(1);
+               regmap_field_read(ocelot->regfields[SYS_RESET_CFG_MEM_INIT],
+                                 &val);
+       } while (val);
+
+       regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1);
+       regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1);
+
+       ocelot->num_cpu_ports = 1; /* 1 port on the switch, two groups */
+
+       ports = of_get_child_by_name(np, "ethernet-ports");
+       if (!ports) {
+               dev_err(&pdev->dev, "no ethernet-ports child node found\n");
+               return -ENODEV;
+       }
+
+       ocelot->num_phys_ports = of_get_child_count(ports);
+
+       ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
+                                    sizeof(struct ocelot_port *), GFP_KERNEL);
+
+       INIT_LIST_HEAD(&ocelot->multicast);
+       ocelot_init(ocelot);
+
+       ocelot_rmw(ocelot, HSIO_HW_CFG_DEV1G_4_MODE |
+                    HSIO_HW_CFG_DEV1G_6_MODE |
+                    HSIO_HW_CFG_DEV1G_9_MODE,
+                    HSIO_HW_CFG_DEV1G_4_MODE |
+                    HSIO_HW_CFG_DEV1G_6_MODE |
+                    HSIO_HW_CFG_DEV1G_9_MODE,
+                    HSIO_HW_CFG);
+
+       for_each_available_child_of_node(ports, portnp) {
+               struct device_node *phy_node;
+               struct phy_device *phy;
+               struct resource *res;
+               void __iomem *regs;
+               char res_name[8];
+               u32 port;
+
+               if (of_property_read_u32(portnp, "reg", &port))
+                       continue;
+
+               snprintf(res_name, sizeof(res_name), "port%d", port);
+
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  res_name);
+               regs = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(regs))
+                       continue;
+
+               phy_node = of_parse_phandle(portnp, "phy-handle", 0);
+               if (!phy_node)
+                       continue;
+
+               phy = of_phy_find_device(phy_node);
+               if (!phy)
+                       continue;
+
+               err = ocelot_probe_port(ocelot, port, regs, phy);
+               if (err) {
+                       dev_err(&pdev->dev, "failed to probe ports\n");
+                       goto err_probe_ports;
+               }
+       }
+
+       register_netdevice_notifier(&ocelot_netdevice_nb);
+
+       dev_info(&pdev->dev, "Ocelot switch probed\n");
+
+       return 0;
+
+err_probe_ports:
+       return err;
+}
+
+static int mscc_ocelot_remove(struct platform_device *pdev)
+{
+       struct ocelot *ocelot = platform_get_drvdata(pdev);
+
+       ocelot_deinit(ocelot);
+       unregister_netdevice_notifier(&ocelot_netdevice_nb);
+
+       return 0;
+}
+
+static struct platform_driver mscc_ocelot_driver = {
+       .probe = mscc_ocelot_probe,
+       .remove = mscc_ocelot_remove,
+       .driver = {
+               .name = "ocelot-switch",
+               .of_match_table = mscc_ocelot_match,
+       },
+};
+
+module_platform_driver(mscc_ocelot_driver);
+
+MODULE_DESCRIPTION("Microsemi Ocelot switch driver");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/net/ethernet/mscc/ocelot_dev.h b/drivers/net/ethernet/mscc/ocelot_dev.h
new file mode 100644 (file)
index 0000000..0a50d53
--- /dev/null
@@ -0,0 +1,275 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_DEV_H_
+#define _MSCC_OCELOT_DEV_H_
+
+#define DEV_CLOCK_CFG                                     0x0
+
+#define DEV_CLOCK_CFG_MAC_TX_RST                          BIT(7)
+#define DEV_CLOCK_CFG_MAC_RX_RST                          BIT(6)
+#define DEV_CLOCK_CFG_PCS_TX_RST                          BIT(5)
+#define DEV_CLOCK_CFG_PCS_RX_RST                          BIT(4)
+#define DEV_CLOCK_CFG_PORT_RST                            BIT(3)
+#define DEV_CLOCK_CFG_PHY_RST                             BIT(2)
+#define DEV_CLOCK_CFG_LINK_SPEED(x)                       ((x) & GENMASK(1, 0))
+#define DEV_CLOCK_CFG_LINK_SPEED_M                        GENMASK(1, 0)
+
+#define DEV_PORT_MISC                                     0x4
+
+#define DEV_PORT_MISC_FWD_ERROR_ENA                       BIT(4)
+#define DEV_PORT_MISC_FWD_PAUSE_ENA                       BIT(3)
+#define DEV_PORT_MISC_FWD_CTRL_ENA                        BIT(2)
+#define DEV_PORT_MISC_DEV_LOOP_ENA                        BIT(1)
+#define DEV_PORT_MISC_HDX_FAST_DIS                        BIT(0)
+
+#define DEV_EVENTS                                        0x8
+
+#define DEV_EEE_CFG                                       0xc
+
+#define DEV_EEE_CFG_EEE_ENA                               BIT(22)
+#define DEV_EEE_CFG_EEE_TIMER_AGE(x)                      (((x) << 15) & GENMASK(21, 15))
+#define DEV_EEE_CFG_EEE_TIMER_AGE_M                       GENMASK(21, 15)
+#define DEV_EEE_CFG_EEE_TIMER_AGE_X(x)                    (((x) & GENMASK(21, 15)) >> 15)
+#define DEV_EEE_CFG_EEE_TIMER_WAKEUP(x)                   (((x) << 8) & GENMASK(14, 8))
+#define DEV_EEE_CFG_EEE_TIMER_WAKEUP_M                    GENMASK(14, 8)
+#define DEV_EEE_CFG_EEE_TIMER_WAKEUP_X(x)                 (((x) & GENMASK(14, 8)) >> 8)
+#define DEV_EEE_CFG_EEE_TIMER_HOLDOFF(x)                  (((x) << 1) & GENMASK(7, 1))
+#define DEV_EEE_CFG_EEE_TIMER_HOLDOFF_M                   GENMASK(7, 1)
+#define DEV_EEE_CFG_EEE_TIMER_HOLDOFF_X(x)                (((x) & GENMASK(7, 1)) >> 1)
+#define DEV_EEE_CFG_PORT_LPI                              BIT(0)
+
+#define DEV_RX_PATH_DELAY                                 0x10
+
+#define DEV_TX_PATH_DELAY                                 0x14
+
+#define DEV_PTP_PREDICT_CFG                               0x18
+
+#define DEV_PTP_PREDICT_CFG_PTP_PHY_PREDICT_CFG(x)        (((x) << 4) & GENMASK(11, 4))
+#define DEV_PTP_PREDICT_CFG_PTP_PHY_PREDICT_CFG_M         GENMASK(11, 4)
+#define DEV_PTP_PREDICT_CFG_PTP_PHY_PREDICT_CFG_X(x)      (((x) & GENMASK(11, 4)) >> 4)
+#define DEV_PTP_PREDICT_CFG_PTP_PHASE_PREDICT_CFG(x)      ((x) & GENMASK(3, 0))
+#define DEV_PTP_PREDICT_CFG_PTP_PHASE_PREDICT_CFG_M       GENMASK(3, 0)
+
+#define DEV_MAC_ENA_CFG                                   0x1c
+
+#define DEV_MAC_ENA_CFG_RX_ENA                            BIT(4)
+#define DEV_MAC_ENA_CFG_TX_ENA                            BIT(0)
+
+#define DEV_MAC_MODE_CFG                                  0x20
+
+#define DEV_MAC_MODE_CFG_FC_WORD_SYNC_ENA                 BIT(8)
+#define DEV_MAC_MODE_CFG_GIGA_MODE_ENA                    BIT(4)
+#define DEV_MAC_MODE_CFG_FDX_ENA                          BIT(0)
+
+#define DEV_MAC_MAXLEN_CFG                                0x24
+
+#define DEV_MAC_TAGS_CFG                                  0x28
+
+#define DEV_MAC_TAGS_CFG_TAG_ID(x)                        (((x) << 16) & GENMASK(31, 16))
+#define DEV_MAC_TAGS_CFG_TAG_ID_M                         GENMASK(31, 16)
+#define DEV_MAC_TAGS_CFG_TAG_ID_X(x)                      (((x) & GENMASK(31, 16)) >> 16)
+#define DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA                 BIT(2)
+#define DEV_MAC_TAGS_CFG_PB_ENA                           BIT(1)
+#define DEV_MAC_TAGS_CFG_VLAN_AWR_ENA                     BIT(0)
+
+#define DEV_MAC_ADV_CHK_CFG                               0x2c
+
+#define DEV_MAC_ADV_CHK_CFG_LEN_DROP_ENA                  BIT(0)
+
+#define DEV_MAC_IFG_CFG                                   0x30
+
+#define DEV_MAC_IFG_CFG_RESTORE_OLD_IPG_CHECK             BIT(17)
+#define DEV_MAC_IFG_CFG_REDUCED_TX_IFG                    BIT(16)
+#define DEV_MAC_IFG_CFG_TX_IFG(x)                         (((x) << 8) & GENMASK(12, 8))
+#define DEV_MAC_IFG_CFG_TX_IFG_M                          GENMASK(12, 8)
+#define DEV_MAC_IFG_CFG_TX_IFG_X(x)                       (((x) & GENMASK(12, 8)) >> 8)
+#define DEV_MAC_IFG_CFG_RX_IFG2(x)                        (((x) << 4) & GENMASK(7, 4))
+#define DEV_MAC_IFG_CFG_RX_IFG2_M                         GENMASK(7, 4)
+#define DEV_MAC_IFG_CFG_RX_IFG2_X(x)                      (((x) & GENMASK(7, 4)) >> 4)
+#define DEV_MAC_IFG_CFG_RX_IFG1(x)                        ((x) & GENMASK(3, 0))
+#define DEV_MAC_IFG_CFG_RX_IFG1_M                         GENMASK(3, 0)
+
+#define DEV_MAC_HDX_CFG                                   0x34
+
+#define DEV_MAC_HDX_CFG_BYPASS_COL_SYNC                   BIT(26)
+#define DEV_MAC_HDX_CFG_OB_ENA                            BIT(25)
+#define DEV_MAC_HDX_CFG_WEXC_DIS                          BIT(24)
+#define DEV_MAC_HDX_CFG_SEED(x)                           (((x) << 16) & GENMASK(23, 16))
+#define DEV_MAC_HDX_CFG_SEED_M                            GENMASK(23, 16)
+#define DEV_MAC_HDX_CFG_SEED_X(x)                         (((x) & GENMASK(23, 16)) >> 16)
+#define DEV_MAC_HDX_CFG_SEED_LOAD                         BIT(12)
+#define DEV_MAC_HDX_CFG_RETRY_AFTER_EXC_COL_ENA           BIT(8)
+#define DEV_MAC_HDX_CFG_LATE_COL_POS(x)                   ((x) & GENMASK(6, 0))
+#define DEV_MAC_HDX_CFG_LATE_COL_POS_M                    GENMASK(6, 0)
+
+#define DEV_MAC_DBG_CFG                                   0x38
+
+#define DEV_MAC_DBG_CFG_TBI_MODE                          BIT(4)
+#define DEV_MAC_DBG_CFG_IFG_CRS_EXT_CHK_ENA               BIT(0)
+
+#define DEV_MAC_FC_MAC_LOW_CFG                            0x3c
+
+#define DEV_MAC_FC_MAC_HIGH_CFG                           0x40
+
+#define DEV_MAC_STICKY                                    0x44
+
+#define DEV_MAC_STICKY_RX_IPG_SHRINK_STICKY               BIT(9)
+#define DEV_MAC_STICKY_RX_PREAM_SHRINK_STICKY             BIT(8)
+#define DEV_MAC_STICKY_RX_CARRIER_EXT_STICKY              BIT(7)
+#define DEV_MAC_STICKY_RX_CARRIER_EXT_ERR_STICKY          BIT(6)
+#define DEV_MAC_STICKY_RX_JUNK_STICKY                     BIT(5)
+#define DEV_MAC_STICKY_TX_RETRANSMIT_STICKY               BIT(4)
+#define DEV_MAC_STICKY_TX_JAM_STICKY                      BIT(3)
+#define DEV_MAC_STICKY_TX_FIFO_OFLW_STICKY                BIT(2)
+#define DEV_MAC_STICKY_TX_FRM_LEN_OVR_STICKY              BIT(1)
+#define DEV_MAC_STICKY_TX_ABORT_STICKY                    BIT(0)
+
+#define PCS1G_CFG                                         0x48
+
+#define PCS1G_CFG_LINK_STATUS_TYPE                        BIT(4)
+#define PCS1G_CFG_AN_LINK_CTRL_ENA                        BIT(1)
+#define PCS1G_CFG_PCS_ENA                                 BIT(0)
+
+#define PCS1G_MODE_CFG                                    0x4c
+
+#define PCS1G_MODE_CFG_UNIDIR_MODE_ENA                    BIT(4)
+#define PCS1G_MODE_CFG_SGMII_MODE_ENA                     BIT(0)
+
+#define PCS1G_SD_CFG                                      0x50
+
+#define PCS1G_SD_CFG_SD_SEL                               BIT(8)
+#define PCS1G_SD_CFG_SD_POL                               BIT(4)
+#define PCS1G_SD_CFG_SD_ENA                               BIT(0)
+
+#define PCS1G_ANEG_CFG                                    0x54
+
+#define PCS1G_ANEG_CFG_ADV_ABILITY(x)                     (((x) << 16) & GENMASK(31, 16))
+#define PCS1G_ANEG_CFG_ADV_ABILITY_M                      GENMASK(31, 16)
+#define PCS1G_ANEG_CFG_ADV_ABILITY_X(x)                   (((x) & GENMASK(31, 16)) >> 16)
+#define PCS1G_ANEG_CFG_SW_RESOLVE_ENA                     BIT(8)
+#define PCS1G_ANEG_CFG_ANEG_RESTART_ONE_SHOT              BIT(1)
+#define PCS1G_ANEG_CFG_ANEG_ENA                           BIT(0)
+
+#define PCS1G_ANEG_NP_CFG                                 0x58
+
+#define PCS1G_ANEG_NP_CFG_NP_TX(x)                        (((x) << 16) & GENMASK(31, 16))
+#define PCS1G_ANEG_NP_CFG_NP_TX_M                         GENMASK(31, 16)
+#define PCS1G_ANEG_NP_CFG_NP_TX_X(x)                      (((x) & GENMASK(31, 16)) >> 16)
+#define PCS1G_ANEG_NP_CFG_NP_LOADED_ONE_SHOT              BIT(0)
+
+#define PCS1G_LB_CFG                                      0x5c
+
+#define PCS1G_LB_CFG_RA_ENA                               BIT(4)
+#define PCS1G_LB_CFG_GMII_PHY_LB_ENA                      BIT(1)
+#define PCS1G_LB_CFG_TBI_HOST_LB_ENA                      BIT(0)
+
+#define PCS1G_DBG_CFG                                     0x60
+
+#define PCS1G_DBG_CFG_UDLT                                BIT(0)
+
+#define PCS1G_CDET_CFG                                    0x64
+
+#define PCS1G_CDET_CFG_CDET_ENA                           BIT(0)
+
+#define PCS1G_ANEG_STATUS                                 0x68
+
+#define PCS1G_ANEG_STATUS_LP_ADV_ABILITY(x)               (((x) << 16) & GENMASK(31, 16))
+#define PCS1G_ANEG_STATUS_LP_ADV_ABILITY_M                GENMASK(31, 16)
+#define PCS1G_ANEG_STATUS_LP_ADV_ABILITY_X(x)             (((x) & GENMASK(31, 16)) >> 16)
+#define PCS1G_ANEG_STATUS_PR                              BIT(4)
+#define PCS1G_ANEG_STATUS_PAGE_RX_STICKY                  BIT(3)
+#define PCS1G_ANEG_STATUS_ANEG_COMPLETE                   BIT(0)
+
+#define PCS1G_ANEG_NP_STATUS                              0x6c
+
+#define PCS1G_LINK_STATUS                                 0x70
+
+#define PCS1G_LINK_STATUS_DELAY_VAR(x)                    (((x) << 12) & GENMASK(15, 12))
+#define PCS1G_LINK_STATUS_DELAY_VAR_M                     GENMASK(15, 12)
+#define PCS1G_LINK_STATUS_DELAY_VAR_X(x)                  (((x) & GENMASK(15, 12)) >> 12)
+#define PCS1G_LINK_STATUS_SIGNAL_DETECT                   BIT(8)
+#define PCS1G_LINK_STATUS_LINK_STATUS                     BIT(4)
+#define PCS1G_LINK_STATUS_SYNC_STATUS                     BIT(0)
+
+#define PCS1G_LINK_DOWN_CNT                               0x74
+
+#define PCS1G_STICKY                                      0x78
+
+#define PCS1G_STICKY_LINK_DOWN_STICKY                     BIT(4)
+#define PCS1G_STICKY_OUT_OF_SYNC_STICKY                   BIT(0)
+
+#define PCS1G_DEBUG_STATUS                                0x7c
+
+#define PCS1G_LPI_CFG                                     0x80
+
+#define PCS1G_LPI_CFG_QSGMII_MS_SEL                       BIT(20)
+#define PCS1G_LPI_CFG_RX_LPI_OUT_DIS                      BIT(17)
+#define PCS1G_LPI_CFG_LPI_TESTMODE                        BIT(16)
+#define PCS1G_LPI_CFG_LPI_RX_WTIM(x)                      (((x) << 4) & GENMASK(5, 4))
+#define PCS1G_LPI_CFG_LPI_RX_WTIM_M                       GENMASK(5, 4)
+#define PCS1G_LPI_CFG_LPI_RX_WTIM_X(x)                    (((x) & GENMASK(5, 4)) >> 4)
+#define PCS1G_LPI_CFG_TX_ASSERT_LPIDLE                    BIT(0)
+
+#define PCS1G_LPI_WAKE_ERROR_CNT                          0x84
+
+#define PCS1G_LPI_STATUS                                  0x88
+
+#define PCS1G_LPI_STATUS_RX_LPI_FAIL                      BIT(16)
+#define PCS1G_LPI_STATUS_RX_LPI_EVENT_STICKY              BIT(12)
+#define PCS1G_LPI_STATUS_RX_QUIET                         BIT(9)
+#define PCS1G_LPI_STATUS_RX_LPI_MODE                      BIT(8)
+#define PCS1G_LPI_STATUS_TX_LPI_EVENT_STICKY              BIT(4)
+#define PCS1G_LPI_STATUS_TX_QUIET                         BIT(1)
+#define PCS1G_LPI_STATUS_TX_LPI_MODE                      BIT(0)
+
+#define PCS1G_TSTPAT_MODE_CFG                             0x8c
+
+#define PCS1G_TSTPAT_STATUS                               0x90
+
+#define PCS1G_TSTPAT_STATUS_JTP_ERR_CNT(x)                (((x) << 8) & GENMASK(15, 8))
+#define PCS1G_TSTPAT_STATUS_JTP_ERR_CNT_M                 GENMASK(15, 8)
+#define PCS1G_TSTPAT_STATUS_JTP_ERR_CNT_X(x)              (((x) & GENMASK(15, 8)) >> 8)
+#define PCS1G_TSTPAT_STATUS_JTP_ERR                       BIT(4)
+#define PCS1G_TSTPAT_STATUS_JTP_LOCK                      BIT(0)
+
+#define DEV_PCS_FX100_CFG                                 0x94
+
+#define DEV_PCS_FX100_CFG_SD_SEL                          BIT(26)
+#define DEV_PCS_FX100_CFG_SD_POL                          BIT(25)
+#define DEV_PCS_FX100_CFG_SD_ENA                          BIT(24)
+#define DEV_PCS_FX100_CFG_LOOPBACK_ENA                    BIT(20)
+#define DEV_PCS_FX100_CFG_SWAP_MII_ENA                    BIT(16)
+#define DEV_PCS_FX100_CFG_RXBITSEL(x)                     (((x) << 12) & GENMASK(15, 12))
+#define DEV_PCS_FX100_CFG_RXBITSEL_M                      GENMASK(15, 12)
+#define DEV_PCS_FX100_CFG_RXBITSEL_X(x)                   (((x) & GENMASK(15, 12)) >> 12)
+#define DEV_PCS_FX100_CFG_SIGDET_CFG(x)                   (((x) << 9) & GENMASK(10, 9))
+#define DEV_PCS_FX100_CFG_SIGDET_CFG_M                    GENMASK(10, 9)
+#define DEV_PCS_FX100_CFG_SIGDET_CFG_X(x)                 (((x) & GENMASK(10, 9)) >> 9)
+#define DEV_PCS_FX100_CFG_LINKHYST_TM_ENA                 BIT(8)
+#define DEV_PCS_FX100_CFG_LINKHYSTTIMER(x)                (((x) << 4) & GENMASK(7, 4))
+#define DEV_PCS_FX100_CFG_LINKHYSTTIMER_M                 GENMASK(7, 4)
+#define DEV_PCS_FX100_CFG_LINKHYSTTIMER_X(x)              (((x) & GENMASK(7, 4)) >> 4)
+#define DEV_PCS_FX100_CFG_UNIDIR_MODE_ENA                 BIT(3)
+#define DEV_PCS_FX100_CFG_FEFCHK_ENA                      BIT(2)
+#define DEV_PCS_FX100_CFG_FEFGEN_ENA                      BIT(1)
+#define DEV_PCS_FX100_CFG_PCS_ENA                         BIT(0)
+
+#define DEV_PCS_FX100_STATUS                              0x98
+
+#define DEV_PCS_FX100_STATUS_EDGE_POS_PTP(x)              (((x) << 8) & GENMASK(11, 8))
+#define DEV_PCS_FX100_STATUS_EDGE_POS_PTP_M               GENMASK(11, 8)
+#define DEV_PCS_FX100_STATUS_EDGE_POS_PTP_X(x)            (((x) & GENMASK(11, 8)) >> 8)
+#define DEV_PCS_FX100_STATUS_PCS_ERROR_STICKY             BIT(7)
+#define DEV_PCS_FX100_STATUS_FEF_FOUND_STICKY             BIT(6)
+#define DEV_PCS_FX100_STATUS_SSD_ERROR_STICKY             BIT(5)
+#define DEV_PCS_FX100_STATUS_SYNC_LOST_STICKY             BIT(4)
+#define DEV_PCS_FX100_STATUS_FEF_STATUS                   BIT(2)
+#define DEV_PCS_FX100_STATUS_SIGNAL_DETECT                BIT(1)
+#define DEV_PCS_FX100_STATUS_SYNC_STATUS                  BIT(0)
+
+#endif
diff --git a/drivers/net/ethernet/mscc/ocelot_dev_gmii.h b/drivers/net/ethernet/mscc/ocelot_dev_gmii.h
new file mode 100644 (file)
index 0000000..6aa40ea
--- /dev/null
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_DEV_GMII_H_
+#define _MSCC_OCELOT_DEV_GMII_H_
+
+#define DEV_GMII_PORT_MODE_CLOCK_CFG                      0x0
+
+#define DEV_GMII_PORT_MODE_CLOCK_CFG_MAC_TX_RST           BIT(5)
+#define DEV_GMII_PORT_MODE_CLOCK_CFG_MAC_RX_RST           BIT(4)
+#define DEV_GMII_PORT_MODE_CLOCK_CFG_PORT_RST             BIT(3)
+#define DEV_GMII_PORT_MODE_CLOCK_CFG_PHY_RST              BIT(2)
+#define DEV_GMII_PORT_MODE_CLOCK_CFG_LINK_SPEED(x)        ((x) & GENMASK(1, 0))
+#define DEV_GMII_PORT_MODE_CLOCK_CFG_LINK_SPEED_M         GENMASK(1, 0)
+
+#define DEV_GMII_PORT_MODE_PORT_MISC                      0x4
+
+#define DEV_GMII_PORT_MODE_PORT_MISC_MPLS_RX_ENA          BIT(5)
+#define DEV_GMII_PORT_MODE_PORT_MISC_FWD_ERROR_ENA        BIT(4)
+#define DEV_GMII_PORT_MODE_PORT_MISC_FWD_PAUSE_ENA        BIT(3)
+#define DEV_GMII_PORT_MODE_PORT_MISC_FWD_CTRL_ENA         BIT(2)
+#define DEV_GMII_PORT_MODE_PORT_MISC_GMII_LOOP_ENA        BIT(1)
+#define DEV_GMII_PORT_MODE_PORT_MISC_DEV_LOOP_ENA         BIT(0)
+
+#define DEV_GMII_PORT_MODE_EVENTS                         0x8
+
+#define DEV_GMII_PORT_MODE_EEE_CFG                        0xc
+
+#define DEV_GMII_PORT_MODE_EEE_CFG_EEE_ENA                BIT(22)
+#define DEV_GMII_PORT_MODE_EEE_CFG_EEE_TIMER_AGE(x)       (((x) << 15) & GENMASK(21, 15))
+#define DEV_GMII_PORT_MODE_EEE_CFG_EEE_TIMER_AGE_M        GENMASK(21, 15)
+#define DEV_GMII_PORT_MODE_EEE_CFG_EEE_TIMER_AGE_X(x)     (((x) & GENMASK(21, 15)) >> 15)
+#define DEV_GMII_PORT_MODE_EEE_CFG_EEE_TIMER_WAKEUP(x)    (((x) << 8) & GENMASK(14, 8))
+#define DEV_GMII_PORT_MODE_EEE_CFG_EEE_TIMER_WAKEUP_M     GENMASK(14, 8)
+#define DEV_GMII_PORT_MODE_EEE_CFG_EEE_TIMER_WAKEUP_X(x)  (((x) & GENMASK(14, 8)) >> 8)
+#define DEV_GMII_PORT_MODE_EEE_CFG_EEE_TIMER_HOLDOFF(x)   (((x) << 1) & GENMASK(7, 1))
+#define DEV_GMII_PORT_MODE_EEE_CFG_EEE_TIMER_HOLDOFF_M    GENMASK(7, 1)
+#define DEV_GMII_PORT_MODE_EEE_CFG_EEE_TIMER_HOLDOFF_X(x) (((x) & GENMASK(7, 1)) >> 1)
+#define DEV_GMII_PORT_MODE_EEE_CFG_PORT_LPI               BIT(0)
+
+#define DEV_GMII_PORT_MODE_RX_PATH_DELAY                  0x10
+
+#define DEV_GMII_PORT_MODE_TX_PATH_DELAY                  0x14
+
+#define DEV_GMII_PORT_MODE_PTP_PREDICT_CFG                0x18
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_ENA_CFG               0x1c
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_ENA_CFG_RX_ENA        BIT(4)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_ENA_CFG_TX_ENA        BIT(0)
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_MODE_CFG              0x20
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_MODE_CFG_FC_WORD_SYNC_ENA BIT(8)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_MODE_CFG_GIGA_MODE_ENA BIT(4)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_MODE_CFG_FDX_ENA      BIT(0)
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_MAXLEN_CFG            0x24
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_TAGS_CFG              0x28
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_TAGS_CFG_TAG_ID(x)    (((x) << 16) & GENMASK(31, 16))
+#define DEV_GMII_MAC_CFG_STATUS_MAC_TAGS_CFG_TAG_ID_M     GENMASK(31, 16)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_TAGS_CFG_TAG_ID_X(x)  (((x) & GENMASK(31, 16)) >> 16)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_TAGS_CFG_PB_ENA       BIT(1)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_TAGS_CFG_VLAN_AWR_ENA BIT(0)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA BIT(2)
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_ADV_CHK_CFG           0x2c
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_ADV_CHK_CFG_LEN_DROP_ENA BIT(0)
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG               0x30
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG_RESTORE_OLD_IPG_CHECK BIT(17)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG_REDUCED_TX_IFG BIT(16)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG_TX_IFG(x)     (((x) << 8) & GENMASK(12, 8))
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG_TX_IFG_M      GENMASK(12, 8)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG_TX_IFG_X(x)   (((x) & GENMASK(12, 8)) >> 8)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG_RX_IFG2(x)    (((x) << 4) & GENMASK(7, 4))
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG_RX_IFG2_M     GENMASK(7, 4)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG_RX_IFG2_X(x)  (((x) & GENMASK(7, 4)) >> 4)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG_RX_IFG1(x)    ((x) & GENMASK(3, 0))
+#define DEV_GMII_MAC_CFG_STATUS_MAC_IFG_CFG_RX_IFG1_M     GENMASK(3, 0)
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG               0x34
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG_BYPASS_COL_SYNC BIT(26)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG_OB_ENA        BIT(25)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG_WEXC_DIS      BIT(24)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG_SEED(x)       (((x) << 16) & GENMASK(23, 16))
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG_SEED_M        GENMASK(23, 16)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG_SEED_X(x)     (((x) & GENMASK(23, 16)) >> 16)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG_SEED_LOAD     BIT(12)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG_RETRY_AFTER_EXC_COL_ENA BIT(8)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG_LATE_COL_POS(x) ((x) & GENMASK(6, 0))
+#define DEV_GMII_MAC_CFG_STATUS_MAC_HDX_CFG_LATE_COL_POS_M GENMASK(6, 0)
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_DBG_CFG               0x38
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_DBG_CFG_TBI_MODE      BIT(4)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_DBG_CFG_IFG_CRS_EXT_CHK_ENA BIT(0)
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_FC_MAC_LOW_CFG        0x3c
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_FC_MAC_HIGH_CFG       0x40
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY                0x44
+
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY_RX_IPG_SHRINK_STICKY BIT(9)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY_RX_PREAM_SHRINK_STICKY BIT(8)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY_RX_CARRIER_EXT_STICKY BIT(7)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY_RX_CARRIER_EXT_ERR_STICKY BIT(6)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY_RX_JUNK_STICKY BIT(5)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY_TX_RETRANSMIT_STICKY BIT(4)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY_TX_JAM_STICKY  BIT(3)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY_TX_FIFO_OFLW_STICKY BIT(2)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY_TX_FRM_LEN_OVR_STICKY BIT(1)
+#define DEV_GMII_MAC_CFG_STATUS_MAC_STICKY_TX_ABORT_STICKY BIT(0)
+
+#define DEV_GMII_MM_CONFIG_ENABLE_CONFIG                  0x48
+
+#define DEV_GMII_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA        BIT(0)
+#define DEV_GMII_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA        BIT(4)
+#define DEV_GMII_MM_CONFIG_ENABLE_CONFIG_KEEP_S_AFTER_D   BIT(8)
+
+#define DEV_GMII_MM_CONFIG_VERIF_CONFIG                   0x4c
+
+#define DEV_GMII_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS    BIT(0)
+#define DEV_GMII_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(x) (((x) << 4) & GENMASK(11, 4))
+#define DEV_GMII_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M GENMASK(11, 4)
+#define DEV_GMII_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(x) (((x) & GENMASK(11, 4)) >> 4)
+#define DEV_GMII_MM_CONFIG_VERIF_CONFIG_VERIF_TIMER_UNITS(x) (((x) << 12) & GENMASK(13, 12))
+#define DEV_GMII_MM_CONFIG_VERIF_CONFIG_VERIF_TIMER_UNITS_M GENMASK(13, 12)
+#define DEV_GMII_MM_CONFIG_VERIF_CONFIG_VERIF_TIMER_UNITS_X(x) (((x) & GENMASK(13, 12)) >> 12)
+
+#define DEV_GMII_MM_STATISTICS_MM_STATUS                  0x50
+
+#define DEV_GMII_MM_STATISTICS_MM_STATUS_PRMPT_ACTIVE_STATUS BIT(0)
+#define DEV_GMII_MM_STATISTICS_MM_STATUS_PRMPT_ACTIVE_STICKY BIT(4)
+#define DEV_GMII_MM_STATISTICS_MM_STATUS_PRMPT_VERIFY_STATE(x) (((x) << 8) & GENMASK(10, 8))
+#define DEV_GMII_MM_STATISTICS_MM_STATUS_PRMPT_VERIFY_STATE_M GENMASK(10, 8)
+#define DEV_GMII_MM_STATISTICS_MM_STATUS_PRMPT_VERIFY_STATE_X(x) (((x) & GENMASK(10, 8)) >> 8)
+#define DEV_GMII_MM_STATISTICS_MM_STATUS_UNEXP_RX_PFRM_STICKY BIT(12)
+#define DEV_GMII_MM_STATISTICS_MM_STATUS_UNEXP_TX_PFRM_STICKY BIT(16)
+#define DEV_GMII_MM_STATISTICS_MM_STATUS_MM_RX_FRAME_STATUS BIT(20)
+#define DEV_GMII_MM_STATISTICS_MM_STATUS_MM_TX_FRAME_STATUS BIT(24)
+#define DEV_GMII_MM_STATISTICS_MM_STATUS_MM_TX_PRMPT_STATUS BIT(28)
+
+#endif
diff --git a/drivers/net/ethernet/mscc/ocelot_hsio.h b/drivers/net/ethernet/mscc/ocelot_hsio.h
new file mode 100644 (file)
index 0000000..d93ddec
--- /dev/null
@@ -0,0 +1,785 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_HSIO_H_
+#define _MSCC_OCELOT_HSIO_H_
+
+#define HSIO_PLL5G_CFG0_ENA_ROT                           BIT(31)
+#define HSIO_PLL5G_CFG0_ENA_LANE                          BIT(30)
+#define HSIO_PLL5G_CFG0_ENA_CLKTREE                       BIT(29)
+#define HSIO_PLL5G_CFG0_DIV4                              BIT(28)
+#define HSIO_PLL5G_CFG0_ENA_LOCK_FINE                     BIT(27)
+#define HSIO_PLL5G_CFG0_SELBGV820(x)                      (((x) << 23) & GENMASK(26, 23))
+#define HSIO_PLL5G_CFG0_SELBGV820_M                       GENMASK(26, 23)
+#define HSIO_PLL5G_CFG0_SELBGV820_X(x)                    (((x) & GENMASK(26, 23)) >> 23)
+#define HSIO_PLL5G_CFG0_LOOP_BW_RES(x)                    (((x) << 18) & GENMASK(22, 18))
+#define HSIO_PLL5G_CFG0_LOOP_BW_RES_M                     GENMASK(22, 18)
+#define HSIO_PLL5G_CFG0_LOOP_BW_RES_X(x)                  (((x) & GENMASK(22, 18)) >> 18)
+#define HSIO_PLL5G_CFG0_SELCPI(x)                         (((x) << 16) & GENMASK(17, 16))
+#define HSIO_PLL5G_CFG0_SELCPI_M                          GENMASK(17, 16)
+#define HSIO_PLL5G_CFG0_SELCPI_X(x)                       (((x) & GENMASK(17, 16)) >> 16)
+#define HSIO_PLL5G_CFG0_ENA_VCO_CONTRH                    BIT(15)
+#define HSIO_PLL5G_CFG0_ENA_CP1                           BIT(14)
+#define HSIO_PLL5G_CFG0_ENA_VCO_BUF                       BIT(13)
+#define HSIO_PLL5G_CFG0_ENA_BIAS                          BIT(12)
+#define HSIO_PLL5G_CFG0_CPU_CLK_DIV(x)                    (((x) << 6) & GENMASK(11, 6))
+#define HSIO_PLL5G_CFG0_CPU_CLK_DIV_M                     GENMASK(11, 6)
+#define HSIO_PLL5G_CFG0_CPU_CLK_DIV_X(x)                  (((x) & GENMASK(11, 6)) >> 6)
+#define HSIO_PLL5G_CFG0_CORE_CLK_DIV(x)                   ((x) & GENMASK(5, 0))
+#define HSIO_PLL5G_CFG0_CORE_CLK_DIV_M                    GENMASK(5, 0)
+
+#define HSIO_PLL5G_CFG1_ENA_DIRECT                        BIT(18)
+#define HSIO_PLL5G_CFG1_ROT_SPEED                         BIT(17)
+#define HSIO_PLL5G_CFG1_ROT_DIR                           BIT(16)
+#define HSIO_PLL5G_CFG1_READBACK_DATA_SEL                 BIT(15)
+#define HSIO_PLL5G_CFG1_RC_ENABLE                         BIT(14)
+#define HSIO_PLL5G_CFG1_RC_CTRL_DATA(x)                   (((x) << 6) & GENMASK(13, 6))
+#define HSIO_PLL5G_CFG1_RC_CTRL_DATA_M                    GENMASK(13, 6)
+#define HSIO_PLL5G_CFG1_RC_CTRL_DATA_X(x)                 (((x) & GENMASK(13, 6)) >> 6)
+#define HSIO_PLL5G_CFG1_QUARTER_RATE                      BIT(5)
+#define HSIO_PLL5G_CFG1_PWD_TX                            BIT(4)
+#define HSIO_PLL5G_CFG1_PWD_RX                            BIT(3)
+#define HSIO_PLL5G_CFG1_OUT_OF_RANGE_RECAL_ENA            BIT(2)
+#define HSIO_PLL5G_CFG1_HALF_RATE                         BIT(1)
+#define HSIO_PLL5G_CFG1_FORCE_SET_ENA                     BIT(0)
+
+#define HSIO_PLL5G_CFG2_ENA_TEST_MODE                     BIT(30)
+#define HSIO_PLL5G_CFG2_ENA_PFD_IN_FLIP                   BIT(29)
+#define HSIO_PLL5G_CFG2_ENA_VCO_NREF_TESTOUT              BIT(28)
+#define HSIO_PLL5G_CFG2_ENA_FBTESTOUT                     BIT(27)
+#define HSIO_PLL5G_CFG2_ENA_RCPLL                         BIT(26)
+#define HSIO_PLL5G_CFG2_ENA_CP2                           BIT(25)
+#define HSIO_PLL5G_CFG2_ENA_CLK_BYPASS1                   BIT(24)
+#define HSIO_PLL5G_CFG2_AMPC_SEL(x)                       (((x) << 16) & GENMASK(23, 16))
+#define HSIO_PLL5G_CFG2_AMPC_SEL_M                        GENMASK(23, 16)
+#define HSIO_PLL5G_CFG2_AMPC_SEL_X(x)                     (((x) & GENMASK(23, 16)) >> 16)
+#define HSIO_PLL5G_CFG2_ENA_CLK_BYPASS                    BIT(15)
+#define HSIO_PLL5G_CFG2_PWD_AMPCTRL_N                     BIT(14)
+#define HSIO_PLL5G_CFG2_ENA_AMPCTRL                       BIT(13)
+#define HSIO_PLL5G_CFG2_ENA_AMP_CTRL_FORCE                BIT(12)
+#define HSIO_PLL5G_CFG2_FRC_FSM_POR                       BIT(11)
+#define HSIO_PLL5G_CFG2_DISABLE_FSM_POR                   BIT(10)
+#define HSIO_PLL5G_CFG2_GAIN_TEST(x)                      (((x) << 5) & GENMASK(9, 5))
+#define HSIO_PLL5G_CFG2_GAIN_TEST_M                       GENMASK(9, 5)
+#define HSIO_PLL5G_CFG2_GAIN_TEST_X(x)                    (((x) & GENMASK(9, 5)) >> 5)
+#define HSIO_PLL5G_CFG2_EN_RESET_OVERRUN                  BIT(4)
+#define HSIO_PLL5G_CFG2_EN_RESET_LIM_DET                  BIT(3)
+#define HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET                  BIT(2)
+#define HSIO_PLL5G_CFG2_DISABLE_FSM                       BIT(1)
+#define HSIO_PLL5G_CFG2_ENA_GAIN_TEST                     BIT(0)
+
+#define HSIO_PLL5G_CFG3_TEST_ANA_OUT_SEL(x)               (((x) << 22) & GENMASK(23, 22))
+#define HSIO_PLL5G_CFG3_TEST_ANA_OUT_SEL_M                GENMASK(23, 22)
+#define HSIO_PLL5G_CFG3_TEST_ANA_OUT_SEL_X(x)             (((x) & GENMASK(23, 22)) >> 22)
+#define HSIO_PLL5G_CFG3_TESTOUT_SEL(x)                    (((x) << 19) & GENMASK(21, 19))
+#define HSIO_PLL5G_CFG3_TESTOUT_SEL_M                     GENMASK(21, 19)
+#define HSIO_PLL5G_CFG3_TESTOUT_SEL_X(x)                  (((x) & GENMASK(21, 19)) >> 19)
+#define HSIO_PLL5G_CFG3_ENA_ANA_TEST_OUT                  BIT(18)
+#define HSIO_PLL5G_CFG3_ENA_TEST_OUT                      BIT(17)
+#define HSIO_PLL5G_CFG3_SEL_FBDCLK                        BIT(16)
+#define HSIO_PLL5G_CFG3_SEL_CML_CMOS_PFD                  BIT(15)
+#define HSIO_PLL5G_CFG3_RST_FB_N                          BIT(14)
+#define HSIO_PLL5G_CFG3_FORCE_VCO_CONTRH                  BIT(13)
+#define HSIO_PLL5G_CFG3_FORCE_LO                          BIT(12)
+#define HSIO_PLL5G_CFG3_FORCE_HI                          BIT(11)
+#define HSIO_PLL5G_CFG3_FORCE_ENA                         BIT(10)
+#define HSIO_PLL5G_CFG3_FORCE_CP                          BIT(9)
+#define HSIO_PLL5G_CFG3_FBDIVSEL_TST_ENA                  BIT(8)
+#define HSIO_PLL5G_CFG3_FBDIVSEL(x)                       ((x) & GENMASK(7, 0))
+#define HSIO_PLL5G_CFG3_FBDIVSEL_M                        GENMASK(7, 0)
+
+#define HSIO_PLL5G_CFG4_IB_BIAS_CTRL(x)                   (((x) << 16) & GENMASK(23, 16))
+#define HSIO_PLL5G_CFG4_IB_BIAS_CTRL_M                    GENMASK(23, 16)
+#define HSIO_PLL5G_CFG4_IB_BIAS_CTRL_X(x)                 (((x) & GENMASK(23, 16)) >> 16)
+#define HSIO_PLL5G_CFG4_IB_CTRL(x)                        ((x) & GENMASK(15, 0))
+#define HSIO_PLL5G_CFG4_IB_CTRL_M                         GENMASK(15, 0)
+
+#define HSIO_PLL5G_CFG5_OB_BIAS_CTRL(x)                   (((x) << 16) & GENMASK(23, 16))
+#define HSIO_PLL5G_CFG5_OB_BIAS_CTRL_M                    GENMASK(23, 16)
+#define HSIO_PLL5G_CFG5_OB_BIAS_CTRL_X(x)                 (((x) & GENMASK(23, 16)) >> 16)
+#define HSIO_PLL5G_CFG5_OB_CTRL(x)                        ((x) & GENMASK(15, 0))
+#define HSIO_PLL5G_CFG5_OB_CTRL_M                         GENMASK(15, 0)
+
+#define HSIO_PLL5G_CFG6_REFCLK_SEL_SRC                    BIT(23)
+#define HSIO_PLL5G_CFG6_REFCLK_SEL(x)                     (((x) << 20) & GENMASK(22, 20))
+#define HSIO_PLL5G_CFG6_REFCLK_SEL_M                      GENMASK(22, 20)
+#define HSIO_PLL5G_CFG6_REFCLK_SEL_X(x)                   (((x) & GENMASK(22, 20)) >> 20)
+#define HSIO_PLL5G_CFG6_REFCLK_SRC                        BIT(19)
+#define HSIO_PLL5G_CFG6_POR_DEL_SEL(x)                    (((x) << 16) & GENMASK(17, 16))
+#define HSIO_PLL5G_CFG6_POR_DEL_SEL_M                     GENMASK(17, 16)
+#define HSIO_PLL5G_CFG6_POR_DEL_SEL_X(x)                  (((x) & GENMASK(17, 16)) >> 16)
+#define HSIO_PLL5G_CFG6_DIV125REF_SEL(x)                  (((x) << 8) & GENMASK(15, 8))
+#define HSIO_PLL5G_CFG6_DIV125REF_SEL_M                   GENMASK(15, 8)
+#define HSIO_PLL5G_CFG6_DIV125REF_SEL_X(x)                (((x) & GENMASK(15, 8)) >> 8)
+#define HSIO_PLL5G_CFG6_ENA_REFCLKC2                      BIT(7)
+#define HSIO_PLL5G_CFG6_ENA_FBCLKC2                       BIT(6)
+#define HSIO_PLL5G_CFG6_DDR_CLK_DIV(x)                    ((x) & GENMASK(5, 0))
+#define HSIO_PLL5G_CFG6_DDR_CLK_DIV_M                     GENMASK(5, 0)
+
+#define HSIO_PLL5G_STATUS0_RANGE_LIM                      BIT(12)
+#define HSIO_PLL5G_STATUS0_OUT_OF_RANGE_ERR               BIT(11)
+#define HSIO_PLL5G_STATUS0_CALIBRATION_ERR                BIT(10)
+#define HSIO_PLL5G_STATUS0_CALIBRATION_DONE               BIT(9)
+#define HSIO_PLL5G_STATUS0_READBACK_DATA(x)               (((x) << 1) & GENMASK(8, 1))
+#define HSIO_PLL5G_STATUS0_READBACK_DATA_M                GENMASK(8, 1)
+#define HSIO_PLL5G_STATUS0_READBACK_DATA_X(x)             (((x) & GENMASK(8, 1)) >> 1)
+#define HSIO_PLL5G_STATUS0_LOCK_STATUS                    BIT(0)
+
+#define HSIO_PLL5G_STATUS1_SIG_DEL(x)                     (((x) << 21) & GENMASK(28, 21))
+#define HSIO_PLL5G_STATUS1_SIG_DEL_M                      GENMASK(28, 21)
+#define HSIO_PLL5G_STATUS1_SIG_DEL_X(x)                   (((x) & GENMASK(28, 21)) >> 21)
+#define HSIO_PLL5G_STATUS1_GAIN_STAT(x)                   (((x) << 16) & GENMASK(20, 16))
+#define HSIO_PLL5G_STATUS1_GAIN_STAT_M                    GENMASK(20, 16)
+#define HSIO_PLL5G_STATUS1_GAIN_STAT_X(x)                 (((x) & GENMASK(20, 16)) >> 16)
+#define HSIO_PLL5G_STATUS1_FBCNT_DIF(x)                   (((x) << 4) & GENMASK(13, 4))
+#define HSIO_PLL5G_STATUS1_FBCNT_DIF_M                    GENMASK(13, 4)
+#define HSIO_PLL5G_STATUS1_FBCNT_DIF_X(x)                 (((x) & GENMASK(13, 4)) >> 4)
+#define HSIO_PLL5G_STATUS1_FSM_STAT(x)                    (((x) << 1) & GENMASK(3, 1))
+#define HSIO_PLL5G_STATUS1_FSM_STAT_M                     GENMASK(3, 1)
+#define HSIO_PLL5G_STATUS1_FSM_STAT_X(x)                  (((x) & GENMASK(3, 1)) >> 1)
+#define HSIO_PLL5G_STATUS1_FSM_LOCK                       BIT(0)
+
+#define HSIO_PLL5G_BIST_CFG0_PLLB_START_BIST              BIT(31)
+#define HSIO_PLL5G_BIST_CFG0_PLLB_MEAS_MODE               BIT(30)
+#define HSIO_PLL5G_BIST_CFG0_PLLB_LOCK_REPEAT(x)          (((x) << 20) & GENMASK(23, 20))
+#define HSIO_PLL5G_BIST_CFG0_PLLB_LOCK_REPEAT_M           GENMASK(23, 20)
+#define HSIO_PLL5G_BIST_CFG0_PLLB_LOCK_REPEAT_X(x)        (((x) & GENMASK(23, 20)) >> 20)
+#define HSIO_PLL5G_BIST_CFG0_PLLB_LOCK_UNCERT(x)          (((x) << 16) & GENMASK(19, 16))
+#define HSIO_PLL5G_BIST_CFG0_PLLB_LOCK_UNCERT_M           GENMASK(19, 16)
+#define HSIO_PLL5G_BIST_CFG0_PLLB_LOCK_UNCERT_X(x)        (((x) & GENMASK(19, 16)) >> 16)
+#define HSIO_PLL5G_BIST_CFG0_PLLB_DIV_FACTOR_PRE(x)       ((x) & GENMASK(15, 0))
+#define HSIO_PLL5G_BIST_CFG0_PLLB_DIV_FACTOR_PRE_M        GENMASK(15, 0)
+
+#define HSIO_PLL5G_BIST_STAT0_PLLB_FSM_STAT(x)            (((x) << 4) & GENMASK(7, 4))
+#define HSIO_PLL5G_BIST_STAT0_PLLB_FSM_STAT_M             GENMASK(7, 4)
+#define HSIO_PLL5G_BIST_STAT0_PLLB_FSM_STAT_X(x)          (((x) & GENMASK(7, 4)) >> 4)
+#define HSIO_PLL5G_BIST_STAT0_PLLB_BUSY                   BIT(2)
+#define HSIO_PLL5G_BIST_STAT0_PLLB_DONE_N                 BIT(1)
+#define HSIO_PLL5G_BIST_STAT0_PLLB_FAIL                   BIT(0)
+
+#define HSIO_PLL5G_BIST_STAT1_PLLB_CNT_OUT(x)             (((x) << 16) & GENMASK(31, 16))
+#define HSIO_PLL5G_BIST_STAT1_PLLB_CNT_OUT_M              GENMASK(31, 16)
+#define HSIO_PLL5G_BIST_STAT1_PLLB_CNT_OUT_X(x)           (((x) & GENMASK(31, 16)) >> 16)
+#define HSIO_PLL5G_BIST_STAT1_PLLB_CNT_REF_DIFF(x)        ((x) & GENMASK(15, 0))
+#define HSIO_PLL5G_BIST_STAT1_PLLB_CNT_REF_DIFF_M         GENMASK(15, 0)
+
+#define HSIO_RCOMP_CFG0_PWD_ENA                           BIT(13)
+#define HSIO_RCOMP_CFG0_RUN_CAL                           BIT(12)
+#define HSIO_RCOMP_CFG0_SPEED_SEL(x)                      (((x) << 10) & GENMASK(11, 10))
+#define HSIO_RCOMP_CFG0_SPEED_SEL_M                       GENMASK(11, 10)
+#define HSIO_RCOMP_CFG0_SPEED_SEL_X(x)                    (((x) & GENMASK(11, 10)) >> 10)
+#define HSIO_RCOMP_CFG0_MODE_SEL(x)                       (((x) << 8) & GENMASK(9, 8))
+#define HSIO_RCOMP_CFG0_MODE_SEL_M                        GENMASK(9, 8)
+#define HSIO_RCOMP_CFG0_MODE_SEL_X(x)                     (((x) & GENMASK(9, 8)) >> 8)
+#define HSIO_RCOMP_CFG0_FORCE_ENA                         BIT(4)
+#define HSIO_RCOMP_CFG0_RCOMP_VAL(x)                      ((x) & GENMASK(3, 0))
+#define HSIO_RCOMP_CFG0_RCOMP_VAL_M                       GENMASK(3, 0)
+
+#define HSIO_RCOMP_STATUS_BUSY                            BIT(12)
+#define HSIO_RCOMP_STATUS_DELTA_ALERT                     BIT(7)
+#define HSIO_RCOMP_STATUS_RCOMP(x)                        ((x) & GENMASK(3, 0))
+#define HSIO_RCOMP_STATUS_RCOMP_M                         GENMASK(3, 0)
+
+#define HSIO_SYNC_ETH_CFG_RSZ                             0x4
+
+#define HSIO_SYNC_ETH_CFG_SEL_RECO_CLK_SRC(x)             (((x) << 4) & GENMASK(7, 4))
+#define HSIO_SYNC_ETH_CFG_SEL_RECO_CLK_SRC_M              GENMASK(7, 4)
+#define HSIO_SYNC_ETH_CFG_SEL_RECO_CLK_SRC_X(x)           (((x) & GENMASK(7, 4)) >> 4)
+#define HSIO_SYNC_ETH_CFG_SEL_RECO_CLK_DIV(x)             (((x) << 1) & GENMASK(3, 1))
+#define HSIO_SYNC_ETH_CFG_SEL_RECO_CLK_DIV_M              GENMASK(3, 1)
+#define HSIO_SYNC_ETH_CFG_SEL_RECO_CLK_DIV_X(x)           (((x) & GENMASK(3, 1)) >> 1)
+#define HSIO_SYNC_ETH_CFG_RECO_CLK_ENA                    BIT(0)
+
+#define HSIO_SYNC_ETH_PLL_CFG_PLL_AUTO_SQUELCH_ENA        BIT(0)
+
+#define HSIO_S1G_DES_CFG_DES_PHS_CTRL(x)                  (((x) << 13) & GENMASK(16, 13))
+#define HSIO_S1G_DES_CFG_DES_PHS_CTRL_M                   GENMASK(16, 13)
+#define HSIO_S1G_DES_CFG_DES_PHS_CTRL_X(x)                (((x) & GENMASK(16, 13)) >> 13)
+#define HSIO_S1G_DES_CFG_DES_CPMD_SEL(x)                  (((x) << 11) & GENMASK(12, 11))
+#define HSIO_S1G_DES_CFG_DES_CPMD_SEL_M                   GENMASK(12, 11)
+#define HSIO_S1G_DES_CFG_DES_CPMD_SEL_X(x)                (((x) & GENMASK(12, 11)) >> 11)
+#define HSIO_S1G_DES_CFG_DES_MBTR_CTRL(x)                 (((x) << 8) & GENMASK(10, 8))
+#define HSIO_S1G_DES_CFG_DES_MBTR_CTRL_M                  GENMASK(10, 8)
+#define HSIO_S1G_DES_CFG_DES_MBTR_CTRL_X(x)               (((x) & GENMASK(10, 8)) >> 8)
+#define HSIO_S1G_DES_CFG_DES_BW_ANA(x)                    (((x) << 5) & GENMASK(7, 5))
+#define HSIO_S1G_DES_CFG_DES_BW_ANA_M                     GENMASK(7, 5)
+#define HSIO_S1G_DES_CFG_DES_BW_ANA_X(x)                  (((x) & GENMASK(7, 5)) >> 5)
+#define HSIO_S1G_DES_CFG_DES_SWAP_ANA                     BIT(4)
+#define HSIO_S1G_DES_CFG_DES_BW_HYST(x)                   (((x) << 1) & GENMASK(3, 1))
+#define HSIO_S1G_DES_CFG_DES_BW_HYST_M                    GENMASK(3, 1)
+#define HSIO_S1G_DES_CFG_DES_BW_HYST_X(x)                 (((x) & GENMASK(3, 1)) >> 1)
+#define HSIO_S1G_DES_CFG_DES_SWAP_HYST                    BIT(0)
+
+#define HSIO_S1G_IB_CFG_IB_FX100_ENA                      BIT(27)
+#define HSIO_S1G_IB_CFG_ACJTAG_HYST(x)                    (((x) << 24) & GENMASK(26, 24))
+#define HSIO_S1G_IB_CFG_ACJTAG_HYST_M                     GENMASK(26, 24)
+#define HSIO_S1G_IB_CFG_ACJTAG_HYST_X(x)                  (((x) & GENMASK(26, 24)) >> 24)
+#define HSIO_S1G_IB_CFG_IB_DET_LEV(x)                     (((x) << 19) & GENMASK(21, 19))
+#define HSIO_S1G_IB_CFG_IB_DET_LEV_M                      GENMASK(21, 19)
+#define HSIO_S1G_IB_CFG_IB_DET_LEV_X(x)                   (((x) & GENMASK(21, 19)) >> 19)
+#define HSIO_S1G_IB_CFG_IB_HYST_LEV                       BIT(14)
+#define HSIO_S1G_IB_CFG_IB_ENA_CMV_TERM                   BIT(13)
+#define HSIO_S1G_IB_CFG_IB_ENA_DC_COUPLING                BIT(12)
+#define HSIO_S1G_IB_CFG_IB_ENA_DETLEV                     BIT(11)
+#define HSIO_S1G_IB_CFG_IB_ENA_HYST                       BIT(10)
+#define HSIO_S1G_IB_CFG_IB_ENA_OFFSET_COMP                BIT(9)
+#define HSIO_S1G_IB_CFG_IB_EQ_GAIN(x)                     (((x) << 6) & GENMASK(8, 6))
+#define HSIO_S1G_IB_CFG_IB_EQ_GAIN_M                      GENMASK(8, 6)
+#define HSIO_S1G_IB_CFG_IB_EQ_GAIN_X(x)                   (((x) & GENMASK(8, 6)) >> 6)
+#define HSIO_S1G_IB_CFG_IB_SEL_CORNER_FREQ(x)             (((x) << 4) & GENMASK(5, 4))
+#define HSIO_S1G_IB_CFG_IB_SEL_CORNER_FREQ_M              GENMASK(5, 4)
+#define HSIO_S1G_IB_CFG_IB_SEL_CORNER_FREQ_X(x)           (((x) & GENMASK(5, 4)) >> 4)
+#define HSIO_S1G_IB_CFG_IB_RESISTOR_CTRL(x)               ((x) & GENMASK(3, 0))
+#define HSIO_S1G_IB_CFG_IB_RESISTOR_CTRL_M                GENMASK(3, 0)
+
+#define HSIO_S1G_OB_CFG_OB_SLP(x)                         (((x) << 17) & GENMASK(18, 17))
+#define HSIO_S1G_OB_CFG_OB_SLP_M                          GENMASK(18, 17)
+#define HSIO_S1G_OB_CFG_OB_SLP_X(x)                       (((x) & GENMASK(18, 17)) >> 17)
+#define HSIO_S1G_OB_CFG_OB_AMP_CTRL(x)                    (((x) << 13) & GENMASK(16, 13))
+#define HSIO_S1G_OB_CFG_OB_AMP_CTRL_M                     GENMASK(16, 13)
+#define HSIO_S1G_OB_CFG_OB_AMP_CTRL_X(x)                  (((x) & GENMASK(16, 13)) >> 13)
+#define HSIO_S1G_OB_CFG_OB_CMM_BIAS_CTRL(x)               (((x) << 10) & GENMASK(12, 10))
+#define HSIO_S1G_OB_CFG_OB_CMM_BIAS_CTRL_M                GENMASK(12, 10)
+#define HSIO_S1G_OB_CFG_OB_CMM_BIAS_CTRL_X(x)             (((x) & GENMASK(12, 10)) >> 10)
+#define HSIO_S1G_OB_CFG_OB_DIS_VCM_CTRL                   BIT(9)
+#define HSIO_S1G_OB_CFG_OB_EN_MEAS_VREG                   BIT(8)
+#define HSIO_S1G_OB_CFG_OB_VCM_CTRL(x)                    (((x) << 4) & GENMASK(7, 4))
+#define HSIO_S1G_OB_CFG_OB_VCM_CTRL_M                     GENMASK(7, 4)
+#define HSIO_S1G_OB_CFG_OB_VCM_CTRL_X(x)                  (((x) & GENMASK(7, 4)) >> 4)
+#define HSIO_S1G_OB_CFG_OB_RESISTOR_CTRL(x)               ((x) & GENMASK(3, 0))
+#define HSIO_S1G_OB_CFG_OB_RESISTOR_CTRL_M                GENMASK(3, 0)
+
+#define HSIO_S1G_SER_CFG_SER_IDLE                         BIT(9)
+#define HSIO_S1G_SER_CFG_SER_DEEMPH                       BIT(8)
+#define HSIO_S1G_SER_CFG_SER_CPMD_SEL                     BIT(7)
+#define HSIO_S1G_SER_CFG_SER_SWAP_CPMD                    BIT(6)
+#define HSIO_S1G_SER_CFG_SER_ALISEL(x)                    (((x) << 4) & GENMASK(5, 4))
+#define HSIO_S1G_SER_CFG_SER_ALISEL_M                     GENMASK(5, 4)
+#define HSIO_S1G_SER_CFG_SER_ALISEL_X(x)                  (((x) & GENMASK(5, 4)) >> 4)
+#define HSIO_S1G_SER_CFG_SER_ENHYS                        BIT(3)
+#define HSIO_S1G_SER_CFG_SER_BIG_WIN                      BIT(2)
+#define HSIO_S1G_SER_CFG_SER_EN_WIN                       BIT(1)
+#define HSIO_S1G_SER_CFG_SER_ENALI                        BIT(0)
+
+#define HSIO_S1G_COMMON_CFG_SYS_RST                       BIT(31)
+#define HSIO_S1G_COMMON_CFG_SE_AUTO_SQUELCH_ENA           BIT(21)
+#define HSIO_S1G_COMMON_CFG_ENA_LANE                      BIT(18)
+#define HSIO_S1G_COMMON_CFG_PWD_RX                        BIT(17)
+#define HSIO_S1G_COMMON_CFG_PWD_TX                        BIT(16)
+#define HSIO_S1G_COMMON_CFG_LANE_CTRL(x)                  (((x) << 13) & GENMASK(15, 13))
+#define HSIO_S1G_COMMON_CFG_LANE_CTRL_M                   GENMASK(15, 13)
+#define HSIO_S1G_COMMON_CFG_LANE_CTRL_X(x)                (((x) & GENMASK(15, 13)) >> 13)
+#define HSIO_S1G_COMMON_CFG_ENA_DIRECT                    BIT(12)
+#define HSIO_S1G_COMMON_CFG_ENA_ELOOP                     BIT(11)
+#define HSIO_S1G_COMMON_CFG_ENA_FLOOP                     BIT(10)
+#define HSIO_S1G_COMMON_CFG_ENA_ILOOP                     BIT(9)
+#define HSIO_S1G_COMMON_CFG_ENA_PLOOP                     BIT(8)
+#define HSIO_S1G_COMMON_CFG_HRATE                         BIT(7)
+#define HSIO_S1G_COMMON_CFG_IF_MODE                       BIT(0)
+
+#define HSIO_S1G_PLL_CFG_PLL_ENA_FB_DIV2                  BIT(22)
+#define HSIO_S1G_PLL_CFG_PLL_ENA_RC_DIV2                  BIT(21)
+#define HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA(x)             (((x) << 8) & GENMASK(15, 8))
+#define HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA_M              GENMASK(15, 8)
+#define HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA_X(x)           (((x) & GENMASK(15, 8)) >> 8)
+#define HSIO_S1G_PLL_CFG_PLL_FSM_ENA                      BIT(7)
+#define HSIO_S1G_PLL_CFG_PLL_FSM_FORCE_SET_ENA            BIT(6)
+#define HSIO_S1G_PLL_CFG_PLL_FSM_OOR_RECAL_ENA            BIT(5)
+#define HSIO_S1G_PLL_CFG_PLL_RB_DATA_SEL                  BIT(3)
+
+#define HSIO_S1G_PLL_STATUS_PLL_CAL_NOT_DONE              BIT(12)
+#define HSIO_S1G_PLL_STATUS_PLL_CAL_ERR                   BIT(11)
+#define HSIO_S1G_PLL_STATUS_PLL_OUT_OF_RANGE_ERR          BIT(10)
+#define HSIO_S1G_PLL_STATUS_PLL_RB_DATA(x)                ((x) & GENMASK(7, 0))
+#define HSIO_S1G_PLL_STATUS_PLL_RB_DATA_M                 GENMASK(7, 0)
+
+#define HSIO_S1G_DFT_CFG0_LAZYBIT                         BIT(31)
+#define HSIO_S1G_DFT_CFG0_INV_DIS                         BIT(23)
+#define HSIO_S1G_DFT_CFG0_PRBS_SEL(x)                     (((x) << 20) & GENMASK(21, 20))
+#define HSIO_S1G_DFT_CFG0_PRBS_SEL_M                      GENMASK(21, 20)
+#define HSIO_S1G_DFT_CFG0_PRBS_SEL_X(x)                   (((x) & GENMASK(21, 20)) >> 20)
+#define HSIO_S1G_DFT_CFG0_TEST_MODE(x)                    (((x) << 16) & GENMASK(18, 16))
+#define HSIO_S1G_DFT_CFG0_TEST_MODE_M                     GENMASK(18, 16)
+#define HSIO_S1G_DFT_CFG0_TEST_MODE_X(x)                  (((x) & GENMASK(18, 16)) >> 16)
+#define HSIO_S1G_DFT_CFG0_RX_PHS_CORR_DIS                 BIT(4)
+#define HSIO_S1G_DFT_CFG0_RX_PDSENS_ENA                   BIT(3)
+#define HSIO_S1G_DFT_CFG0_RX_DFT_ENA                      BIT(2)
+#define HSIO_S1G_DFT_CFG0_TX_DFT_ENA                      BIT(0)
+
+#define HSIO_S1G_DFT_CFG1_TX_JITTER_AMPL(x)               (((x) << 8) & GENMASK(17, 8))
+#define HSIO_S1G_DFT_CFG1_TX_JITTER_AMPL_M                GENMASK(17, 8)
+#define HSIO_S1G_DFT_CFG1_TX_JITTER_AMPL_X(x)             (((x) & GENMASK(17, 8)) >> 8)
+#define HSIO_S1G_DFT_CFG1_TX_STEP_FREQ(x)                 (((x) << 4) & GENMASK(7, 4))
+#define HSIO_S1G_DFT_CFG1_TX_STEP_FREQ_M                  GENMASK(7, 4)
+#define HSIO_S1G_DFT_CFG1_TX_STEP_FREQ_X(x)               (((x) & GENMASK(7, 4)) >> 4)
+#define HSIO_S1G_DFT_CFG1_TX_JI_ENA                       BIT(3)
+#define HSIO_S1G_DFT_CFG1_TX_WAVEFORM_SEL                 BIT(2)
+#define HSIO_S1G_DFT_CFG1_TX_FREQOFF_DIR                  BIT(1)
+#define HSIO_S1G_DFT_CFG1_TX_FREQOFF_ENA                  BIT(0)
+
+#define HSIO_S1G_DFT_CFG2_RX_JITTER_AMPL(x)               (((x) << 8) & GENMASK(17, 8))
+#define HSIO_S1G_DFT_CFG2_RX_JITTER_AMPL_M                GENMASK(17, 8)
+#define HSIO_S1G_DFT_CFG2_RX_JITTER_AMPL_X(x)             (((x) & GENMASK(17, 8)) >> 8)
+#define HSIO_S1G_DFT_CFG2_RX_STEP_FREQ(x)                 (((x) << 4) & GENMASK(7, 4))
+#define HSIO_S1G_DFT_CFG2_RX_STEP_FREQ_M                  GENMASK(7, 4)
+#define HSIO_S1G_DFT_CFG2_RX_STEP_FREQ_X(x)               (((x) & GENMASK(7, 4)) >> 4)
+#define HSIO_S1G_DFT_CFG2_RX_JI_ENA                       BIT(3)
+#define HSIO_S1G_DFT_CFG2_RX_WAVEFORM_SEL                 BIT(2)
+#define HSIO_S1G_DFT_CFG2_RX_FREQOFF_DIR                  BIT(1)
+#define HSIO_S1G_DFT_CFG2_RX_FREQOFF_ENA                  BIT(0)
+
+#define HSIO_S1G_RC_PLL_BIST_CFG_PLL_BIST_ENA             BIT(20)
+#define HSIO_S1G_RC_PLL_BIST_CFG_PLL_BIST_FBS_HIGH(x)     (((x) << 16) & GENMASK(17, 16))
+#define HSIO_S1G_RC_PLL_BIST_CFG_PLL_BIST_FBS_HIGH_M      GENMASK(17, 16)
+#define HSIO_S1G_RC_PLL_BIST_CFG_PLL_BIST_FBS_HIGH_X(x)   (((x) & GENMASK(17, 16)) >> 16)
+#define HSIO_S1G_RC_PLL_BIST_CFG_PLL_BIST_HIGH(x)         (((x) << 8) & GENMASK(15, 8))
+#define HSIO_S1G_RC_PLL_BIST_CFG_PLL_BIST_HIGH_M          GENMASK(15, 8)
+#define HSIO_S1G_RC_PLL_BIST_CFG_PLL_BIST_HIGH_X(x)       (((x) & GENMASK(15, 8)) >> 8)
+#define HSIO_S1G_RC_PLL_BIST_CFG_PLL_BIST_LOW(x)          ((x) & GENMASK(7, 0))
+#define HSIO_S1G_RC_PLL_BIST_CFG_PLL_BIST_LOW_M           GENMASK(7, 0)
+
+#define HSIO_S1G_MISC_CFG_DES_100FX_KICK_MODE(x)          (((x) << 11) & GENMASK(12, 11))
+#define HSIO_S1G_MISC_CFG_DES_100FX_KICK_MODE_M           GENMASK(12, 11)
+#define HSIO_S1G_MISC_CFG_DES_100FX_KICK_MODE_X(x)        (((x) & GENMASK(12, 11)) >> 11)
+#define HSIO_S1G_MISC_CFG_DES_100FX_CPMD_SWAP             BIT(10)
+#define HSIO_S1G_MISC_CFG_DES_100FX_CPMD_MODE             BIT(9)
+#define HSIO_S1G_MISC_CFG_DES_100FX_CPMD_ENA              BIT(8)
+#define HSIO_S1G_MISC_CFG_RX_LPI_MODE_ENA                 BIT(5)
+#define HSIO_S1G_MISC_CFG_TX_LPI_MODE_ENA                 BIT(4)
+#define HSIO_S1G_MISC_CFG_RX_DATA_INV_ENA                 BIT(3)
+#define HSIO_S1G_MISC_CFG_TX_DATA_INV_ENA                 BIT(2)
+#define HSIO_S1G_MISC_CFG_LANE_RST                        BIT(0)
+
+#define HSIO_S1G_DFT_STATUS_PLL_BIST_NOT_DONE             BIT(7)
+#define HSIO_S1G_DFT_STATUS_PLL_BIST_FAILED               BIT(6)
+#define HSIO_S1G_DFT_STATUS_PLL_BIST_TIMEOUT_ERR          BIT(5)
+#define HSIO_S1G_DFT_STATUS_BIST_ACTIVE                   BIT(3)
+#define HSIO_S1G_DFT_STATUS_BIST_NOSYNC                   BIT(2)
+#define HSIO_S1G_DFT_STATUS_BIST_COMPLETE_N               BIT(1)
+#define HSIO_S1G_DFT_STATUS_BIST_ERROR                    BIT(0)
+
+#define HSIO_S1G_MISC_STATUS_DES_100FX_PHASE_SEL          BIT(0)
+
+#define HSIO_MCB_S1G_ADDR_CFG_SERDES1G_WR_ONE_SHOT        BIT(31)
+#define HSIO_MCB_S1G_ADDR_CFG_SERDES1G_RD_ONE_SHOT        BIT(30)
+#define HSIO_MCB_S1G_ADDR_CFG_SERDES1G_ADDR(x)            ((x) & GENMASK(8, 0))
+#define HSIO_MCB_S1G_ADDR_CFG_SERDES1G_ADDR_M             GENMASK(8, 0)
+
+#define HSIO_S6G_DIG_CFG_GP(x)                            (((x) << 16) & GENMASK(18, 16))
+#define HSIO_S6G_DIG_CFG_GP_M                             GENMASK(18, 16)
+#define HSIO_S6G_DIG_CFG_GP_X(x)                          (((x) & GENMASK(18, 16)) >> 16)
+#define HSIO_S6G_DIG_CFG_TX_BIT_DOUBLING_MODE_ENA         BIT(7)
+#define HSIO_S6G_DIG_CFG_SIGDET_TESTMODE                  BIT(6)
+#define HSIO_S6G_DIG_CFG_SIGDET_AST(x)                    (((x) << 3) & GENMASK(5, 3))
+#define HSIO_S6G_DIG_CFG_SIGDET_AST_M                     GENMASK(5, 3)
+#define HSIO_S6G_DIG_CFG_SIGDET_AST_X(x)                  (((x) & GENMASK(5, 3)) >> 3)
+#define HSIO_S6G_DIG_CFG_SIGDET_DST(x)                    ((x) & GENMASK(2, 0))
+#define HSIO_S6G_DIG_CFG_SIGDET_DST_M                     GENMASK(2, 0)
+
+#define HSIO_S6G_DFT_CFG0_LAZYBIT                         BIT(31)
+#define HSIO_S6G_DFT_CFG0_INV_DIS                         BIT(23)
+#define HSIO_S6G_DFT_CFG0_PRBS_SEL(x)                     (((x) << 20) & GENMASK(21, 20))
+#define HSIO_S6G_DFT_CFG0_PRBS_SEL_M                      GENMASK(21, 20)
+#define HSIO_S6G_DFT_CFG0_PRBS_SEL_X(x)                   (((x) & GENMASK(21, 20)) >> 20)
+#define HSIO_S6G_DFT_CFG0_TEST_MODE(x)                    (((x) << 16) & GENMASK(18, 16))
+#define HSIO_S6G_DFT_CFG0_TEST_MODE_M                     GENMASK(18, 16)
+#define HSIO_S6G_DFT_CFG0_TEST_MODE_X(x)                  (((x) & GENMASK(18, 16)) >> 16)
+#define HSIO_S6G_DFT_CFG0_RX_PHS_CORR_DIS                 BIT(4)
+#define HSIO_S6G_DFT_CFG0_RX_PDSENS_ENA                   BIT(3)
+#define HSIO_S6G_DFT_CFG0_RX_DFT_ENA                      BIT(2)
+#define HSIO_S6G_DFT_CFG0_TX_DFT_ENA                      BIT(0)
+
+#define HSIO_S6G_DFT_CFG1_TX_JITTER_AMPL(x)               (((x) << 8) & GENMASK(17, 8))
+#define HSIO_S6G_DFT_CFG1_TX_JITTER_AMPL_M                GENMASK(17, 8)
+#define HSIO_S6G_DFT_CFG1_TX_JITTER_AMPL_X(x)             (((x) & GENMASK(17, 8)) >> 8)
+#define HSIO_S6G_DFT_CFG1_TX_STEP_FREQ(x)                 (((x) << 4) & GENMASK(7, 4))
+#define HSIO_S6G_DFT_CFG1_TX_STEP_FREQ_M                  GENMASK(7, 4)
+#define HSIO_S6G_DFT_CFG1_TX_STEP_FREQ_X(x)               (((x) & GENMASK(7, 4)) >> 4)
+#define HSIO_S6G_DFT_CFG1_TX_JI_ENA                       BIT(3)
+#define HSIO_S6G_DFT_CFG1_TX_WAVEFORM_SEL                 BIT(2)
+#define HSIO_S6G_DFT_CFG1_TX_FREQOFF_DIR                  BIT(1)
+#define HSIO_S6G_DFT_CFG1_TX_FREQOFF_ENA                  BIT(0)
+
+#define HSIO_S6G_DFT_CFG2_RX_JITTER_AMPL(x)               (((x) << 8) & GENMASK(17, 8))
+#define HSIO_S6G_DFT_CFG2_RX_JITTER_AMPL_M                GENMASK(17, 8)
+#define HSIO_S6G_DFT_CFG2_RX_JITTER_AMPL_X(x)             (((x) & GENMASK(17, 8)) >> 8)
+#define HSIO_S6G_DFT_CFG2_RX_STEP_FREQ(x)                 (((x) << 4) & GENMASK(7, 4))
+#define HSIO_S6G_DFT_CFG2_RX_STEP_FREQ_M                  GENMASK(7, 4)
+#define HSIO_S6G_DFT_CFG2_RX_STEP_FREQ_X(x)               (((x) & GENMASK(7, 4)) >> 4)
+#define HSIO_S6G_DFT_CFG2_RX_JI_ENA                       BIT(3)
+#define HSIO_S6G_DFT_CFG2_RX_WAVEFORM_SEL                 BIT(2)
+#define HSIO_S6G_DFT_CFG2_RX_FREQOFF_DIR                  BIT(1)
+#define HSIO_S6G_DFT_CFG2_RX_FREQOFF_ENA                  BIT(0)
+
+#define HSIO_S6G_RC_PLL_BIST_CFG_PLL_BIST_ENA             BIT(20)
+#define HSIO_S6G_RC_PLL_BIST_CFG_PLL_BIST_FBS_HIGH(x)     (((x) << 16) & GENMASK(19, 16))
+#define HSIO_S6G_RC_PLL_BIST_CFG_PLL_BIST_FBS_HIGH_M      GENMASK(19, 16)
+#define HSIO_S6G_RC_PLL_BIST_CFG_PLL_BIST_FBS_HIGH_X(x)   (((x) & GENMASK(19, 16)) >> 16)
+#define HSIO_S6G_RC_PLL_BIST_CFG_PLL_BIST_HIGH(x)         (((x) << 8) & GENMASK(15, 8))
+#define HSIO_S6G_RC_PLL_BIST_CFG_PLL_BIST_HIGH_M          GENMASK(15, 8)
+#define HSIO_S6G_RC_PLL_BIST_CFG_PLL_BIST_HIGH_X(x)       (((x) & GENMASK(15, 8)) >> 8)
+#define HSIO_S6G_RC_PLL_BIST_CFG_PLL_BIST_LOW(x)          ((x) & GENMASK(7, 0))
+#define HSIO_S6G_RC_PLL_BIST_CFG_PLL_BIST_LOW_M           GENMASK(7, 0)
+
+#define HSIO_S6G_MISC_CFG_SEL_RECO_CLK(x)                 (((x) << 13) & GENMASK(14, 13))
+#define HSIO_S6G_MISC_CFG_SEL_RECO_CLK_M                  GENMASK(14, 13)
+#define HSIO_S6G_MISC_CFG_SEL_RECO_CLK_X(x)               (((x) & GENMASK(14, 13)) >> 13)
+#define HSIO_S6G_MISC_CFG_DES_100FX_KICK_MODE(x)          (((x) << 11) & GENMASK(12, 11))
+#define HSIO_S6G_MISC_CFG_DES_100FX_KICK_MODE_M           GENMASK(12, 11)
+#define HSIO_S6G_MISC_CFG_DES_100FX_KICK_MODE_X(x)        (((x) & GENMASK(12, 11)) >> 11)
+#define HSIO_S6G_MISC_CFG_DES_100FX_CPMD_SWAP             BIT(10)
+#define HSIO_S6G_MISC_CFG_DES_100FX_CPMD_MODE             BIT(9)
+#define HSIO_S6G_MISC_CFG_DES_100FX_CPMD_ENA              BIT(8)
+#define HSIO_S6G_MISC_CFG_RX_BUS_FLIP_ENA                 BIT(7)
+#define HSIO_S6G_MISC_CFG_TX_BUS_FLIP_ENA                 BIT(6)
+#define HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA                 BIT(5)
+#define HSIO_S6G_MISC_CFG_TX_LPI_MODE_ENA                 BIT(4)
+#define HSIO_S6G_MISC_CFG_RX_DATA_INV_ENA                 BIT(3)
+#define HSIO_S6G_MISC_CFG_TX_DATA_INV_ENA                 BIT(2)
+#define HSIO_S6G_MISC_CFG_LANE_RST                        BIT(0)
+
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_POST0(x)               (((x) << 23) & GENMASK(28, 23))
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_POST0_M                GENMASK(28, 23)
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_POST0_X(x)             (((x) & GENMASK(28, 23)) >> 23)
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_POST1(x)               (((x) << 18) & GENMASK(22, 18))
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_POST1_M                GENMASK(22, 18)
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_POST1_X(x)             (((x) & GENMASK(22, 18)) >> 18)
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_PREC(x)                (((x) << 13) & GENMASK(17, 13))
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_PREC_M                 GENMASK(17, 13)
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_PREC_X(x)              (((x) & GENMASK(17, 13)) >> 13)
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_ENA_CAS(x)             (((x) << 6) & GENMASK(8, 6))
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_ENA_CAS_M              GENMASK(8, 6)
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_ENA_CAS_X(x)           (((x) & GENMASK(8, 6)) >> 6)
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_LEV(x)                 ((x) & GENMASK(5, 0))
+#define HSIO_S6G_OB_ANEG_CFG_AN_OB_LEV_M                  GENMASK(5, 0)
+
+#define HSIO_S6G_DFT_STATUS_PRBS_SYNC_STAT                BIT(8)
+#define HSIO_S6G_DFT_STATUS_PLL_BIST_NOT_DONE             BIT(7)
+#define HSIO_S6G_DFT_STATUS_PLL_BIST_FAILED               BIT(6)
+#define HSIO_S6G_DFT_STATUS_PLL_BIST_TIMEOUT_ERR          BIT(5)
+#define HSIO_S6G_DFT_STATUS_BIST_ACTIVE                   BIT(3)
+#define HSIO_S6G_DFT_STATUS_BIST_NOSYNC                   BIT(2)
+#define HSIO_S6G_DFT_STATUS_BIST_COMPLETE_N               BIT(1)
+#define HSIO_S6G_DFT_STATUS_BIST_ERROR                    BIT(0)
+
+#define HSIO_S6G_MISC_STATUS_DES_100FX_PHASE_SEL          BIT(0)
+
+#define HSIO_S6G_DES_CFG_DES_PHS_CTRL(x)                  (((x) << 13) & GENMASK(16, 13))
+#define HSIO_S6G_DES_CFG_DES_PHS_CTRL_M                   GENMASK(16, 13)
+#define HSIO_S6G_DES_CFG_DES_PHS_CTRL_X(x)                (((x) & GENMASK(16, 13)) >> 13)
+#define HSIO_S6G_DES_CFG_DES_MBTR_CTRL(x)                 (((x) << 10) & GENMASK(12, 10))
+#define HSIO_S6G_DES_CFG_DES_MBTR_CTRL_M                  GENMASK(12, 10)
+#define HSIO_S6G_DES_CFG_DES_MBTR_CTRL_X(x)               (((x) & GENMASK(12, 10)) >> 10)
+#define HSIO_S6G_DES_CFG_DES_CPMD_SEL(x)                  (((x) << 8) & GENMASK(9, 8))
+#define HSIO_S6G_DES_CFG_DES_CPMD_SEL_M                   GENMASK(9, 8)
+#define HSIO_S6G_DES_CFG_DES_CPMD_SEL_X(x)                (((x) & GENMASK(9, 8)) >> 8)
+#define HSIO_S6G_DES_CFG_DES_BW_HYST(x)                   (((x) << 5) & GENMASK(7, 5))
+#define HSIO_S6G_DES_CFG_DES_BW_HYST_M                    GENMASK(7, 5)
+#define HSIO_S6G_DES_CFG_DES_BW_HYST_X(x)                 (((x) & GENMASK(7, 5)) >> 5)
+#define HSIO_S6G_DES_CFG_DES_SWAP_HYST                    BIT(4)
+#define HSIO_S6G_DES_CFG_DES_BW_ANA(x)                    (((x) << 1) & GENMASK(3, 1))
+#define HSIO_S6G_DES_CFG_DES_BW_ANA_M                     GENMASK(3, 1)
+#define HSIO_S6G_DES_CFG_DES_BW_ANA_X(x)                  (((x) & GENMASK(3, 1)) >> 1)
+#define HSIO_S6G_DES_CFG_DES_SWAP_ANA                     BIT(0)
+
+#define HSIO_S6G_IB_CFG_IB_SOFSI(x)                       (((x) << 29) & GENMASK(30, 29))
+#define HSIO_S6G_IB_CFG_IB_SOFSI_M                        GENMASK(30, 29)
+#define HSIO_S6G_IB_CFG_IB_SOFSI_X(x)                     (((x) & GENMASK(30, 29)) >> 29)
+#define HSIO_S6G_IB_CFG_IB_VBULK_SEL                      BIT(28)
+#define HSIO_S6G_IB_CFG_IB_RTRM_ADJ(x)                    (((x) << 24) & GENMASK(27, 24))
+#define HSIO_S6G_IB_CFG_IB_RTRM_ADJ_M                     GENMASK(27, 24)
+#define HSIO_S6G_IB_CFG_IB_RTRM_ADJ_X(x)                  (((x) & GENMASK(27, 24)) >> 24)
+#define HSIO_S6G_IB_CFG_IB_ICML_ADJ(x)                    (((x) << 20) & GENMASK(23, 20))
+#define HSIO_S6G_IB_CFG_IB_ICML_ADJ_M                     GENMASK(23, 20)
+#define HSIO_S6G_IB_CFG_IB_ICML_ADJ_X(x)                  (((x) & GENMASK(23, 20)) >> 20)
+#define HSIO_S6G_IB_CFG_IB_TERM_MODE_SEL(x)               (((x) << 18) & GENMASK(19, 18))
+#define HSIO_S6G_IB_CFG_IB_TERM_MODE_SEL_M                GENMASK(19, 18)
+#define HSIO_S6G_IB_CFG_IB_TERM_MODE_SEL_X(x)             (((x) & GENMASK(19, 18)) >> 18)
+#define HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(x)             (((x) << 15) & GENMASK(17, 15))
+#define HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M              GENMASK(17, 15)
+#define HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_X(x)           (((x) & GENMASK(17, 15)) >> 15)
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_HP(x)              (((x) << 13) & GENMASK(14, 13))
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_HP_M               GENMASK(14, 13)
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_HP_X(x)            (((x) & GENMASK(14, 13)) >> 13)
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_MID(x)             (((x) << 11) & GENMASK(12, 11))
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_MID_M              GENMASK(12, 11)
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_MID_X(x)           (((x) & GENMASK(12, 11)) >> 11)
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_LP(x)              (((x) << 9) & GENMASK(10, 9))
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_LP_M               GENMASK(10, 9)
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_LP_X(x)            (((x) & GENMASK(10, 9)) >> 9)
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(x)          (((x) << 7) & GENMASK(8, 7))
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M           GENMASK(8, 7)
+#define HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_X(x)        (((x) & GENMASK(8, 7)) >> 7)
+#define HSIO_S6G_IB_CFG_IB_ANA_TEST_ENA                   BIT(6)
+#define HSIO_S6G_IB_CFG_IB_SIG_DET_ENA                    BIT(5)
+#define HSIO_S6G_IB_CFG_IB_CONCUR                         BIT(4)
+#define HSIO_S6G_IB_CFG_IB_CAL_ENA                        BIT(3)
+#define HSIO_S6G_IB_CFG_IB_SAM_ENA                        BIT(2)
+#define HSIO_S6G_IB_CFG_IB_EQZ_ENA                        BIT(1)
+#define HSIO_S6G_IB_CFG_IB_REG_ENA                        BIT(0)
+
+#define HSIO_S6G_IB_CFG1_IB_TJTAG(x)                      (((x) << 17) & GENMASK(21, 17))
+#define HSIO_S6G_IB_CFG1_IB_TJTAG_M                       GENMASK(21, 17)
+#define HSIO_S6G_IB_CFG1_IB_TJTAG_X(x)                    (((x) & GENMASK(21, 17)) >> 17)
+#define HSIO_S6G_IB_CFG1_IB_TSDET(x)                      (((x) << 12) & GENMASK(16, 12))
+#define HSIO_S6G_IB_CFG1_IB_TSDET_M                       GENMASK(16, 12)
+#define HSIO_S6G_IB_CFG1_IB_TSDET_X(x)                    (((x) & GENMASK(16, 12)) >> 12)
+#define HSIO_S6G_IB_CFG1_IB_SCALY(x)                      (((x) << 8) & GENMASK(11, 8))
+#define HSIO_S6G_IB_CFG1_IB_SCALY_M                       GENMASK(11, 8)
+#define HSIO_S6G_IB_CFG1_IB_SCALY_X(x)                    (((x) & GENMASK(11, 8)) >> 8)
+#define HSIO_S6G_IB_CFG1_IB_FILT_HP                       BIT(7)
+#define HSIO_S6G_IB_CFG1_IB_FILT_MID                      BIT(6)
+#define HSIO_S6G_IB_CFG1_IB_FILT_LP                       BIT(5)
+#define HSIO_S6G_IB_CFG1_IB_FILT_OFFSET                   BIT(4)
+#define HSIO_S6G_IB_CFG1_IB_FRC_HP                        BIT(3)
+#define HSIO_S6G_IB_CFG1_IB_FRC_MID                       BIT(2)
+#define HSIO_S6G_IB_CFG1_IB_FRC_LP                        BIT(1)
+#define HSIO_S6G_IB_CFG1_IB_FRC_OFFSET                    BIT(0)
+
+#define HSIO_S6G_IB_CFG2_IB_TINFV(x)                      (((x) << 27) & GENMASK(29, 27))
+#define HSIO_S6G_IB_CFG2_IB_TINFV_M                       GENMASK(29, 27)
+#define HSIO_S6G_IB_CFG2_IB_TINFV_X(x)                    (((x) & GENMASK(29, 27)) >> 27)
+#define HSIO_S6G_IB_CFG2_IB_OINFI(x)                      (((x) << 22) & GENMASK(26, 22))
+#define HSIO_S6G_IB_CFG2_IB_OINFI_M                       GENMASK(26, 22)
+#define HSIO_S6G_IB_CFG2_IB_OINFI_X(x)                    (((x) & GENMASK(26, 22)) >> 22)
+#define HSIO_S6G_IB_CFG2_IB_TAUX(x)                       (((x) << 19) & GENMASK(21, 19))
+#define HSIO_S6G_IB_CFG2_IB_TAUX_M                        GENMASK(21, 19)
+#define HSIO_S6G_IB_CFG2_IB_TAUX_X(x)                     (((x) & GENMASK(21, 19)) >> 19)
+#define HSIO_S6G_IB_CFG2_IB_OINFS(x)                      (((x) << 16) & GENMASK(18, 16))
+#define HSIO_S6G_IB_CFG2_IB_OINFS_M                       GENMASK(18, 16)
+#define HSIO_S6G_IB_CFG2_IB_OINFS_X(x)                    (((x) & GENMASK(18, 16)) >> 16)
+#define HSIO_S6G_IB_CFG2_IB_OCALS(x)                      (((x) << 10) & GENMASK(15, 10))
+#define HSIO_S6G_IB_CFG2_IB_OCALS_M                       GENMASK(15, 10)
+#define HSIO_S6G_IB_CFG2_IB_OCALS_X(x)                    (((x) & GENMASK(15, 10)) >> 10)
+#define HSIO_S6G_IB_CFG2_IB_TCALV(x)                      (((x) << 5) & GENMASK(9, 5))
+#define HSIO_S6G_IB_CFG2_IB_TCALV_M                       GENMASK(9, 5)
+#define HSIO_S6G_IB_CFG2_IB_TCALV_X(x)                    (((x) & GENMASK(9, 5)) >> 5)
+#define HSIO_S6G_IB_CFG2_IB_UMAX(x)                       (((x) << 3) & GENMASK(4, 3))
+#define HSIO_S6G_IB_CFG2_IB_UMAX_M                        GENMASK(4, 3)
+#define HSIO_S6G_IB_CFG2_IB_UMAX_X(x)                     (((x) & GENMASK(4, 3)) >> 3)
+#define HSIO_S6G_IB_CFG2_IB_UREG(x)                       ((x) & GENMASK(2, 0))
+#define HSIO_S6G_IB_CFG2_IB_UREG_M                        GENMASK(2, 0)
+
+#define HSIO_S6G_IB_CFG3_IB_INI_HP(x)                     (((x) << 18) & GENMASK(23, 18))
+#define HSIO_S6G_IB_CFG3_IB_INI_HP_M                      GENMASK(23, 18)
+#define HSIO_S6G_IB_CFG3_IB_INI_HP_X(x)                   (((x) & GENMASK(23, 18)) >> 18)
+#define HSIO_S6G_IB_CFG3_IB_INI_MID(x)                    (((x) << 12) & GENMASK(17, 12))
+#define HSIO_S6G_IB_CFG3_IB_INI_MID_M                     GENMASK(17, 12)
+#define HSIO_S6G_IB_CFG3_IB_INI_MID_X(x)                  (((x) & GENMASK(17, 12)) >> 12)
+#define HSIO_S6G_IB_CFG3_IB_INI_LP(x)                     (((x) << 6) & GENMASK(11, 6))
+#define HSIO_S6G_IB_CFG3_IB_INI_LP_M                      GENMASK(11, 6)
+#define HSIO_S6G_IB_CFG3_IB_INI_LP_X(x)                   (((x) & GENMASK(11, 6)) >> 6)
+#define HSIO_S6G_IB_CFG3_IB_INI_OFFSET(x)                 ((x) & GENMASK(5, 0))
+#define HSIO_S6G_IB_CFG3_IB_INI_OFFSET_M                  GENMASK(5, 0)
+
+#define HSIO_S6G_IB_CFG4_IB_MAX_HP(x)                     (((x) << 18) & GENMASK(23, 18))
+#define HSIO_S6G_IB_CFG4_IB_MAX_HP_M                      GENMASK(23, 18)
+#define HSIO_S6G_IB_CFG4_IB_MAX_HP_X(x)                   (((x) & GENMASK(23, 18)) >> 18)
+#define HSIO_S6G_IB_CFG4_IB_MAX_MID(x)                    (((x) << 12) & GENMASK(17, 12))
+#define HSIO_S6G_IB_CFG4_IB_MAX_MID_M                     GENMASK(17, 12)
+#define HSIO_S6G_IB_CFG4_IB_MAX_MID_X(x)                  (((x) & GENMASK(17, 12)) >> 12)
+#define HSIO_S6G_IB_CFG4_IB_MAX_LP(x)                     (((x) << 6) & GENMASK(11, 6))
+#define HSIO_S6G_IB_CFG4_IB_MAX_LP_M                      GENMASK(11, 6)
+#define HSIO_S6G_IB_CFG4_IB_MAX_LP_X(x)                   (((x) & GENMASK(11, 6)) >> 6)
+#define HSIO_S6G_IB_CFG4_IB_MAX_OFFSET(x)                 ((x) & GENMASK(5, 0))
+#define HSIO_S6G_IB_CFG4_IB_MAX_OFFSET_M                  GENMASK(5, 0)
+
+#define HSIO_S6G_IB_CFG5_IB_MIN_HP(x)                     (((x) << 18) & GENMASK(23, 18))
+#define HSIO_S6G_IB_CFG5_IB_MIN_HP_M                      GENMASK(23, 18)
+#define HSIO_S6G_IB_CFG5_IB_MIN_HP_X(x)                   (((x) & GENMASK(23, 18)) >> 18)
+#define HSIO_S6G_IB_CFG5_IB_MIN_MID(x)                    (((x) << 12) & GENMASK(17, 12))
+#define HSIO_S6G_IB_CFG5_IB_MIN_MID_M                     GENMASK(17, 12)
+#define HSIO_S6G_IB_CFG5_IB_MIN_MID_X(x)                  (((x) & GENMASK(17, 12)) >> 12)
+#define HSIO_S6G_IB_CFG5_IB_MIN_LP(x)                     (((x) << 6) & GENMASK(11, 6))
+#define HSIO_S6G_IB_CFG5_IB_MIN_LP_M                      GENMASK(11, 6)
+#define HSIO_S6G_IB_CFG5_IB_MIN_LP_X(x)                   (((x) & GENMASK(11, 6)) >> 6)
+#define HSIO_S6G_IB_CFG5_IB_MIN_OFFSET(x)                 ((x) & GENMASK(5, 0))
+#define HSIO_S6G_IB_CFG5_IB_MIN_OFFSET_M                  GENMASK(5, 0)
+
+#define HSIO_S6G_OB_CFG_OB_IDLE                           BIT(31)
+#define HSIO_S6G_OB_CFG_OB_ENA1V_MODE                     BIT(30)
+#define HSIO_S6G_OB_CFG_OB_POL                            BIT(29)
+#define HSIO_S6G_OB_CFG_OB_POST0(x)                       (((x) << 23) & GENMASK(28, 23))
+#define HSIO_S6G_OB_CFG_OB_POST0_M                        GENMASK(28, 23)
+#define HSIO_S6G_OB_CFG_OB_POST0_X(x)                     (((x) & GENMASK(28, 23)) >> 23)
+#define HSIO_S6G_OB_CFG_OB_PREC(x)                        (((x) << 18) & GENMASK(22, 18))
+#define HSIO_S6G_OB_CFG_OB_PREC_M                         GENMASK(22, 18)
+#define HSIO_S6G_OB_CFG_OB_PREC_X(x)                      (((x) & GENMASK(22, 18)) >> 18)
+#define HSIO_S6G_OB_CFG_OB_R_ADJ_MUX                      BIT(17)
+#define HSIO_S6G_OB_CFG_OB_R_ADJ_PDR                      BIT(16)
+#define HSIO_S6G_OB_CFG_OB_POST1(x)                       (((x) << 11) & GENMASK(15, 11))
+#define HSIO_S6G_OB_CFG_OB_POST1_M                        GENMASK(15, 11)
+#define HSIO_S6G_OB_CFG_OB_POST1_X(x)                     (((x) & GENMASK(15, 11)) >> 11)
+#define HSIO_S6G_OB_CFG_OB_R_COR                          BIT(10)
+#define HSIO_S6G_OB_CFG_OB_SEL_RCTRL                      BIT(9)
+#define HSIO_S6G_OB_CFG_OB_SR_H                           BIT(8)
+#define HSIO_S6G_OB_CFG_OB_SR(x)                          (((x) << 4) & GENMASK(7, 4))
+#define HSIO_S6G_OB_CFG_OB_SR_M                           GENMASK(7, 4)
+#define HSIO_S6G_OB_CFG_OB_SR_X(x)                        (((x) & GENMASK(7, 4)) >> 4)
+#define HSIO_S6G_OB_CFG_OB_RESISTOR_CTRL(x)               ((x) & GENMASK(3, 0))
+#define HSIO_S6G_OB_CFG_OB_RESISTOR_CTRL_M                GENMASK(3, 0)
+
+#define HSIO_S6G_OB_CFG1_OB_ENA_CAS(x)                    (((x) << 6) & GENMASK(8, 6))
+#define HSIO_S6G_OB_CFG1_OB_ENA_CAS_M                     GENMASK(8, 6)
+#define HSIO_S6G_OB_CFG1_OB_ENA_CAS_X(x)                  (((x) & GENMASK(8, 6)) >> 6)
+#define HSIO_S6G_OB_CFG1_OB_LEV(x)                        ((x) & GENMASK(5, 0))
+#define HSIO_S6G_OB_CFG1_OB_LEV_M                         GENMASK(5, 0)
+
+#define HSIO_S6G_SER_CFG_SER_4TAP_ENA                     BIT(8)
+#define HSIO_S6G_SER_CFG_SER_CPMD_SEL                     BIT(7)
+#define HSIO_S6G_SER_CFG_SER_SWAP_CPMD                    BIT(6)
+#define HSIO_S6G_SER_CFG_SER_ALISEL(x)                    (((x) << 4) & GENMASK(5, 4))
+#define HSIO_S6G_SER_CFG_SER_ALISEL_M                     GENMASK(5, 4)
+#define HSIO_S6G_SER_CFG_SER_ALISEL_X(x)                  (((x) & GENMASK(5, 4)) >> 4)
+#define HSIO_S6G_SER_CFG_SER_ENHYS                        BIT(3)
+#define HSIO_S6G_SER_CFG_SER_BIG_WIN                      BIT(2)
+#define HSIO_S6G_SER_CFG_SER_EN_WIN                       BIT(1)
+#define HSIO_S6G_SER_CFG_SER_ENALI                        BIT(0)
+
+#define HSIO_S6G_COMMON_CFG_SYS_RST                       BIT(17)
+#define HSIO_S6G_COMMON_CFG_SE_DIV2_ENA                   BIT(16)
+#define HSIO_S6G_COMMON_CFG_SE_AUTO_SQUELCH_ENA           BIT(15)
+#define HSIO_S6G_COMMON_CFG_ENA_LANE                      BIT(14)
+#define HSIO_S6G_COMMON_CFG_PWD_RX                        BIT(13)
+#define HSIO_S6G_COMMON_CFG_PWD_TX                        BIT(12)
+#define HSIO_S6G_COMMON_CFG_LANE_CTRL(x)                  (((x) << 9) & GENMASK(11, 9))
+#define HSIO_S6G_COMMON_CFG_LANE_CTRL_M                   GENMASK(11, 9)
+#define HSIO_S6G_COMMON_CFG_LANE_CTRL_X(x)                (((x) & GENMASK(11, 9)) >> 9)
+#define HSIO_S6G_COMMON_CFG_ENA_DIRECT                    BIT(8)
+#define HSIO_S6G_COMMON_CFG_ENA_ELOOP                     BIT(7)
+#define HSIO_S6G_COMMON_CFG_ENA_FLOOP                     BIT(6)
+#define HSIO_S6G_COMMON_CFG_ENA_ILOOP                     BIT(5)
+#define HSIO_S6G_COMMON_CFG_ENA_PLOOP                     BIT(4)
+#define HSIO_S6G_COMMON_CFG_HRATE                         BIT(3)
+#define HSIO_S6G_COMMON_CFG_QRATE                         BIT(2)
+#define HSIO_S6G_COMMON_CFG_IF_MODE(x)                    ((x) & GENMASK(1, 0))
+#define HSIO_S6G_COMMON_CFG_IF_MODE_M                     GENMASK(1, 0)
+
+#define HSIO_S6G_PLL_CFG_PLL_ENA_OFFS(x)                  (((x) << 16) & GENMASK(17, 16))
+#define HSIO_S6G_PLL_CFG_PLL_ENA_OFFS_M                   GENMASK(17, 16)
+#define HSIO_S6G_PLL_CFG_PLL_ENA_OFFS_X(x)                (((x) & GENMASK(17, 16)) >> 16)
+#define HSIO_S6G_PLL_CFG_PLL_DIV4                         BIT(15)
+#define HSIO_S6G_PLL_CFG_PLL_ENA_ROT                      BIT(14)
+#define HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA(x)             (((x) << 6) & GENMASK(13, 6))
+#define HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA_M              GENMASK(13, 6)
+#define HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA_X(x)           (((x) & GENMASK(13, 6)) >> 6)
+#define HSIO_S6G_PLL_CFG_PLL_FSM_ENA                      BIT(5)
+#define HSIO_S6G_PLL_CFG_PLL_FSM_FORCE_SET_ENA            BIT(4)
+#define HSIO_S6G_PLL_CFG_PLL_FSM_OOR_RECAL_ENA            BIT(3)
+#define HSIO_S6G_PLL_CFG_PLL_RB_DATA_SEL                  BIT(2)
+#define HSIO_S6G_PLL_CFG_PLL_ROT_DIR                      BIT(1)
+#define HSIO_S6G_PLL_CFG_PLL_ROT_FRQ                      BIT(0)
+
+#define HSIO_S6G_ACJTAG_CFG_ACJTAG_INIT_DATA_N            BIT(5)
+#define HSIO_S6G_ACJTAG_CFG_ACJTAG_INIT_DATA_P            BIT(4)
+#define HSIO_S6G_ACJTAG_CFG_ACJTAG_INIT_CLK               BIT(3)
+#define HSIO_S6G_ACJTAG_CFG_OB_DIRECT                     BIT(2)
+#define HSIO_S6G_ACJTAG_CFG_ACJTAG_ENA                    BIT(1)
+#define HSIO_S6G_ACJTAG_CFG_JTAG_CTRL_ENA                 BIT(0)
+
+#define HSIO_S6G_GP_CFG_GP_MSB(x)                         (((x) << 16) & GENMASK(31, 16))
+#define HSIO_S6G_GP_CFG_GP_MSB_M                          GENMASK(31, 16)
+#define HSIO_S6G_GP_CFG_GP_MSB_X(x)                       (((x) & GENMASK(31, 16)) >> 16)
+#define HSIO_S6G_GP_CFG_GP_LSB(x)                         ((x) & GENMASK(15, 0))
+#define HSIO_S6G_GP_CFG_GP_LSB_M                          GENMASK(15, 0)
+
+#define HSIO_S6G_IB_STATUS0_IB_CAL_DONE                   BIT(8)
+#define HSIO_S6G_IB_STATUS0_IB_HP_GAIN_ACT                BIT(7)
+#define HSIO_S6G_IB_STATUS0_IB_MID_GAIN_ACT               BIT(6)
+#define HSIO_S6G_IB_STATUS0_IB_LP_GAIN_ACT                BIT(5)
+#define HSIO_S6G_IB_STATUS0_IB_OFFSET_ACT                 BIT(4)
+#define HSIO_S6G_IB_STATUS0_IB_OFFSET_VLD                 BIT(3)
+#define HSIO_S6G_IB_STATUS0_IB_OFFSET_ERR                 BIT(2)
+#define HSIO_S6G_IB_STATUS0_IB_OFFSDIR                    BIT(1)
+#define HSIO_S6G_IB_STATUS0_IB_SIG_DET                    BIT(0)
+
+#define HSIO_S6G_IB_STATUS1_IB_HP_GAIN_STAT(x)            (((x) << 18) & GENMASK(23, 18))
+#define HSIO_S6G_IB_STATUS1_IB_HP_GAIN_STAT_M             GENMASK(23, 18)
+#define HSIO_S6G_IB_STATUS1_IB_HP_GAIN_STAT_X(x)          (((x) & GENMASK(23, 18)) >> 18)
+#define HSIO_S6G_IB_STATUS1_IB_MID_GAIN_STAT(x)           (((x) << 12) & GENMASK(17, 12))
+#define HSIO_S6G_IB_STATUS1_IB_MID_GAIN_STAT_M            GENMASK(17, 12)
+#define HSIO_S6G_IB_STATUS1_IB_MID_GAIN_STAT_X(x)         (((x) & GENMASK(17, 12)) >> 12)
+#define HSIO_S6G_IB_STATUS1_IB_LP_GAIN_STAT(x)            (((x) << 6) & GENMASK(11, 6))
+#define HSIO_S6G_IB_STATUS1_IB_LP_GAIN_STAT_M             GENMASK(11, 6)
+#define HSIO_S6G_IB_STATUS1_IB_LP_GAIN_STAT_X(x)          (((x) & GENMASK(11, 6)) >> 6)
+#define HSIO_S6G_IB_STATUS1_IB_OFFSET_STAT(x)             ((x) & GENMASK(5, 0))
+#define HSIO_S6G_IB_STATUS1_IB_OFFSET_STAT_M              GENMASK(5, 0)
+
+#define HSIO_S6G_ACJTAG_STATUS_ACJTAG_CAPT_DATA_N         BIT(2)
+#define HSIO_S6G_ACJTAG_STATUS_ACJTAG_CAPT_DATA_P         BIT(1)
+#define HSIO_S6G_ACJTAG_STATUS_IB_DIRECT                  BIT(0)
+
+#define HSIO_S6G_PLL_STATUS_PLL_CAL_NOT_DONE              BIT(10)
+#define HSIO_S6G_PLL_STATUS_PLL_CAL_ERR                   BIT(9)
+#define HSIO_S6G_PLL_STATUS_PLL_OUT_OF_RANGE_ERR          BIT(8)
+#define HSIO_S6G_PLL_STATUS_PLL_RB_DATA(x)                ((x) & GENMASK(7, 0))
+#define HSIO_S6G_PLL_STATUS_PLL_RB_DATA_M                 GENMASK(7, 0)
+
+#define HSIO_S6G_REVID_SERDES_REV(x)                      (((x) << 26) & GENMASK(31, 26))
+#define HSIO_S6G_REVID_SERDES_REV_M                       GENMASK(31, 26)
+#define HSIO_S6G_REVID_SERDES_REV_X(x)                    (((x) & GENMASK(31, 26)) >> 26)
+#define HSIO_S6G_REVID_RCPLL_REV(x)                       (((x) << 21) & GENMASK(25, 21))
+#define HSIO_S6G_REVID_RCPLL_REV_M                        GENMASK(25, 21)
+#define HSIO_S6G_REVID_RCPLL_REV_X(x)                     (((x) & GENMASK(25, 21)) >> 21)
+#define HSIO_S6G_REVID_SER_REV(x)                         (((x) << 16) & GENMASK(20, 16))
+#define HSIO_S6G_REVID_SER_REV_M                          GENMASK(20, 16)
+#define HSIO_S6G_REVID_SER_REV_X(x)                       (((x) & GENMASK(20, 16)) >> 16)
+#define HSIO_S6G_REVID_DES_REV(x)                         (((x) << 10) & GENMASK(15, 10))
+#define HSIO_S6G_REVID_DES_REV_M                          GENMASK(15, 10)
+#define HSIO_S6G_REVID_DES_REV_X(x)                       (((x) & GENMASK(15, 10)) >> 10)
+#define HSIO_S6G_REVID_OB_REV(x)                          (((x) << 5) & GENMASK(9, 5))
+#define HSIO_S6G_REVID_OB_REV_M                           GENMASK(9, 5)
+#define HSIO_S6G_REVID_OB_REV_X(x)                        (((x) & GENMASK(9, 5)) >> 5)
+#define HSIO_S6G_REVID_IB_REV(x)                          ((x) & GENMASK(4, 0))
+#define HSIO_S6G_REVID_IB_REV_M                           GENMASK(4, 0)
+
+#define HSIO_MCB_S6G_ADDR_CFG_SERDES6G_WR_ONE_SHOT        BIT(31)
+#define HSIO_MCB_S6G_ADDR_CFG_SERDES6G_RD_ONE_SHOT        BIT(30)
+#define HSIO_MCB_S6G_ADDR_CFG_SERDES6G_ADDR(x)            ((x) & GENMASK(24, 0))
+#define HSIO_MCB_S6G_ADDR_CFG_SERDES6G_ADDR_M             GENMASK(24, 0)
+
+#define HSIO_HW_CFG_DEV2G5_10_MODE                        BIT(6)
+#define HSIO_HW_CFG_DEV1G_9_MODE                          BIT(5)
+#define HSIO_HW_CFG_DEV1G_6_MODE                          BIT(4)
+#define HSIO_HW_CFG_DEV1G_5_MODE                          BIT(3)
+#define HSIO_HW_CFG_DEV1G_4_MODE                          BIT(2)
+#define HSIO_HW_CFG_PCIE_ENA                              BIT(1)
+#define HSIO_HW_CFG_QSGMII_ENA                            BIT(0)
+
+#define HSIO_HW_QSGMII_CFG_SHYST_DIS                      BIT(3)
+#define HSIO_HW_QSGMII_CFG_E_DET_ENA                      BIT(2)
+#define HSIO_HW_QSGMII_CFG_USE_I1_ENA                     BIT(1)
+#define HSIO_HW_QSGMII_CFG_FLIP_LANES                     BIT(0)
+
+#define HSIO_HW_QSGMII_STAT_DELAY_VAR_X200PS(x)           (((x) << 1) & GENMASK(6, 1))
+#define HSIO_HW_QSGMII_STAT_DELAY_VAR_X200PS_M            GENMASK(6, 1)
+#define HSIO_HW_QSGMII_STAT_DELAY_VAR_X200PS_X(x)         (((x) & GENMASK(6, 1)) >> 1)
+#define HSIO_HW_QSGMII_STAT_SYNC                          BIT(0)
+
+#define HSIO_CLK_CFG_CLKDIV_PHY(x)                        (((x) << 1) & GENMASK(8, 1))
+#define HSIO_CLK_CFG_CLKDIV_PHY_M                         GENMASK(8, 1)
+#define HSIO_CLK_CFG_CLKDIV_PHY_X(x)                      (((x) & GENMASK(8, 1)) >> 1)
+#define HSIO_CLK_CFG_CLKDIV_PHY_DIS                       BIT(0)
+
+#define HSIO_TEMP_SENSOR_CTRL_FORCE_TEMP_RD               BIT(5)
+#define HSIO_TEMP_SENSOR_CTRL_FORCE_RUN                   BIT(4)
+#define HSIO_TEMP_SENSOR_CTRL_FORCE_NO_RST                BIT(3)
+#define HSIO_TEMP_SENSOR_CTRL_FORCE_POWER_UP              BIT(2)
+#define HSIO_TEMP_SENSOR_CTRL_FORCE_CLK                   BIT(1)
+#define HSIO_TEMP_SENSOR_CTRL_SAMPLE_ENA                  BIT(0)
+
+#define HSIO_TEMP_SENSOR_CFG_RUN_WID(x)                   (((x) << 8) & GENMASK(15, 8))
+#define HSIO_TEMP_SENSOR_CFG_RUN_WID_M                    GENMASK(15, 8)
+#define HSIO_TEMP_SENSOR_CFG_RUN_WID_X(x)                 (((x) & GENMASK(15, 8)) >> 8)
+#define HSIO_TEMP_SENSOR_CFG_SAMPLE_PER(x)                ((x) & GENMASK(7, 0))
+#define HSIO_TEMP_SENSOR_CFG_SAMPLE_PER_M                 GENMASK(7, 0)
+
+#define HSIO_TEMP_SENSOR_STAT_TEMP_VALID                  BIT(8)
+#define HSIO_TEMP_SENSOR_STAT_TEMP(x)                     ((x) & GENMASK(7, 0))
+#define HSIO_TEMP_SENSOR_STAT_TEMP_M                      GENMASK(7, 0)
+
+#endif
diff --git a/drivers/net/ethernet/mscc/ocelot_io.c b/drivers/net/ethernet/mscc/ocelot_io.c
new file mode 100644 (file)
index 0000000..c6db8ad
--- /dev/null
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include "ocelot.h"
+
+u32 __ocelot_read_ix(struct ocelot *ocelot, u32 reg, u32 offset)
+{
+       u16 target = reg >> TARGET_OFFSET;
+       u32 val;
+
+       WARN_ON(!target);
+
+       regmap_read(ocelot->targets[target],
+                   ocelot->map[target][reg & REG_MASK] + offset, &val);
+       return val;
+}
+EXPORT_SYMBOL(__ocelot_read_ix);
+
+void __ocelot_write_ix(struct ocelot *ocelot, u32 val, u32 reg, u32 offset)
+{
+       u16 target = reg >> TARGET_OFFSET;
+
+       WARN_ON(!target);
+
+       regmap_write(ocelot->targets[target],
+                    ocelot->map[target][reg & REG_MASK] + offset, val);
+}
+EXPORT_SYMBOL(__ocelot_write_ix);
+
+void __ocelot_rmw_ix(struct ocelot *ocelot, u32 val, u32 mask, u32 reg,
+                    u32 offset)
+{
+       u16 target = reg >> TARGET_OFFSET;
+
+       WARN_ON(!target);
+
+       regmap_update_bits(ocelot->targets[target],
+                          ocelot->map[target][reg & REG_MASK] + offset,
+                          mask, val);
+}
+EXPORT_SYMBOL(__ocelot_rmw_ix);
+
+u32 ocelot_port_readl(struct ocelot_port *port, u32 reg)
+{
+       return readl(port->regs + reg);
+}
+EXPORT_SYMBOL(ocelot_port_readl);
+
+void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg)
+{
+       writel(val, port->regs + reg);
+}
+EXPORT_SYMBOL(ocelot_port_writel);
+
+int ocelot_regfields_init(struct ocelot *ocelot,
+                         const struct reg_field *const regfields)
+{
+       unsigned int i;
+       u16 target;
+
+       for (i = 0; i < REGFIELD_MAX; i++) {
+               struct reg_field regfield = {};
+               u32 reg = regfields[i].reg;
+
+               if (!reg)
+                       continue;
+
+               target = regfields[i].reg >> TARGET_OFFSET;
+
+               regfield.reg = ocelot->map[target][reg & REG_MASK];
+               regfield.lsb = regfields[i].lsb;
+               regfield.msb = regfields[i].msb;
+
+               ocelot->regfields[i] =
+               devm_regmap_field_alloc(ocelot->dev,
+                                       ocelot->targets[target],
+                                       regfield);
+
+               if (IS_ERR(ocelot->regfields[i]))
+                       return PTR_ERR(ocelot->regfields[i]);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_regfields_init);
+
+static struct regmap_config ocelot_regmap_config = {
+       .reg_bits       = 32,
+       .val_bits       = 32,
+       .reg_stride     = 4,
+};
+
+struct regmap *ocelot_io_platform_init(struct ocelot *ocelot,
+                                      struct platform_device *pdev,
+                                      const char *name)
+{
+       struct resource *res;
+       void __iomem *regs;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+       regs = devm_ioremap_resource(ocelot->dev, res);
+       if (IS_ERR(regs))
+               return ERR_CAST(regs);
+
+       ocelot_regmap_config.name = name;
+       return devm_regmap_init_mmio(ocelot->dev, regs,
+                                    &ocelot_regmap_config);
+}
+EXPORT_SYMBOL(ocelot_io_platform_init);
diff --git a/drivers/net/ethernet/mscc/ocelot_qs.h b/drivers/net/ethernet/mscc/ocelot_qs.h
new file mode 100644 (file)
index 0000000..d18ae72
--- /dev/null
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_QS_H_
+#define _MSCC_OCELOT_QS_H_
+
+/* TODO handle BE */
+#define XTR_EOF_0          0x00000080U
+#define XTR_EOF_1          0x01000080U
+#define XTR_EOF_2          0x02000080U
+#define XTR_EOF_3          0x03000080U
+#define XTR_PRUNED         0x04000080U
+#define XTR_ABORT          0x05000080U
+#define XTR_ESCAPE         0x06000080U
+#define XTR_NOT_READY      0x07000080U
+#define XTR_VALID_BYTES(x) (4 - (((x) >> 24) & 3))
+
+#define QS_XTR_GRP_CFG_RSZ                                0x4
+
+#define QS_XTR_GRP_CFG_MODE(x)                            (((x) << 2) & GENMASK(3, 2))
+#define QS_XTR_GRP_CFG_MODE_M                             GENMASK(3, 2)
+#define QS_XTR_GRP_CFG_MODE_X(x)                          (((x) & GENMASK(3, 2)) >> 2)
+#define QS_XTR_GRP_CFG_STATUS_WORD_POS                    BIT(1)
+#define QS_XTR_GRP_CFG_BYTE_SWAP                          BIT(0)
+
+#define QS_XTR_RD_RSZ                                     0x4
+
+#define QS_XTR_FRM_PRUNING_RSZ                            0x4
+
+#define QS_XTR_CFG_DP_WM(x)                               (((x) << 5) & GENMASK(7, 5))
+#define QS_XTR_CFG_DP_WM_M                                GENMASK(7, 5)
+#define QS_XTR_CFG_DP_WM_X(x)                             (((x) & GENMASK(7, 5)) >> 5)
+#define QS_XTR_CFG_SCH_WM(x)                              (((x) << 2) & GENMASK(4, 2))
+#define QS_XTR_CFG_SCH_WM_M                               GENMASK(4, 2)
+#define QS_XTR_CFG_SCH_WM_X(x)                            (((x) & GENMASK(4, 2)) >> 2)
+#define QS_XTR_CFG_OFLW_ERR_STICKY(x)                     ((x) & GENMASK(1, 0))
+#define QS_XTR_CFG_OFLW_ERR_STICKY_M                      GENMASK(1, 0)
+
+#define QS_INJ_GRP_CFG_RSZ                                0x4
+
+#define QS_INJ_GRP_CFG_MODE(x)                            (((x) << 2) & GENMASK(3, 2))
+#define QS_INJ_GRP_CFG_MODE_M                             GENMASK(3, 2)
+#define QS_INJ_GRP_CFG_MODE_X(x)                          (((x) & GENMASK(3, 2)) >> 2)
+#define QS_INJ_GRP_CFG_BYTE_SWAP                          BIT(0)
+
+#define QS_INJ_WR_RSZ                                     0x4
+
+#define QS_INJ_CTRL_RSZ                                   0x4
+
+#define QS_INJ_CTRL_GAP_SIZE(x)                           (((x) << 21) & GENMASK(24, 21))
+#define QS_INJ_CTRL_GAP_SIZE_M                            GENMASK(24, 21)
+#define QS_INJ_CTRL_GAP_SIZE_X(x)                         (((x) & GENMASK(24, 21)) >> 21)
+#define QS_INJ_CTRL_ABORT                                 BIT(20)
+#define QS_INJ_CTRL_EOF                                   BIT(19)
+#define QS_INJ_CTRL_SOF                                   BIT(18)
+#define QS_INJ_CTRL_VLD_BYTES(x)                          (((x) << 16) & GENMASK(17, 16))
+#define QS_INJ_CTRL_VLD_BYTES_M                           GENMASK(17, 16)
+#define QS_INJ_CTRL_VLD_BYTES_X(x)                        (((x) & GENMASK(17, 16)) >> 16)
+
+#define QS_INJ_STATUS_WMARK_REACHED(x)                    (((x) << 4) & GENMASK(5, 4))
+#define QS_INJ_STATUS_WMARK_REACHED_M                     GENMASK(5, 4)
+#define QS_INJ_STATUS_WMARK_REACHED_X(x)                  (((x) & GENMASK(5, 4)) >> 4)
+#define QS_INJ_STATUS_FIFO_RDY(x)                         (((x) << 2) & GENMASK(3, 2))
+#define QS_INJ_STATUS_FIFO_RDY_M                          GENMASK(3, 2)
+#define QS_INJ_STATUS_FIFO_RDY_X(x)                       (((x) & GENMASK(3, 2)) >> 2)
+#define QS_INJ_STATUS_INJ_IN_PROGRESS(x)                  ((x) & GENMASK(1, 0))
+#define QS_INJ_STATUS_INJ_IN_PROGRESS_M                   GENMASK(1, 0)
+
+#define QS_INJ_ERR_RSZ                                    0x4
+
+#define QS_INJ_ERR_ABORT_ERR_STICKY                       BIT(1)
+#define QS_INJ_ERR_WR_ERR_STICKY                          BIT(0)
+
+#endif
diff --git a/drivers/net/ethernet/mscc/ocelot_qsys.h b/drivers/net/ethernet/mscc/ocelot_qsys.h
new file mode 100644 (file)
index 0000000..d8c63aa
--- /dev/null
@@ -0,0 +1,270 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_QSYS_H_
+#define _MSCC_OCELOT_QSYS_H_
+
+#define QSYS_PORT_MODE_RSZ                                0x4
+
+#define QSYS_PORT_MODE_DEQUEUE_DIS                        BIT(1)
+#define QSYS_PORT_MODE_DEQUEUE_LATE                       BIT(0)
+
+#define QSYS_SWITCH_PORT_MODE_RSZ                         0x4
+
+#define QSYS_SWITCH_PORT_MODE_PORT_ENA                    BIT(14)
+#define QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(x)             (((x) << 11) & GENMASK(13, 11))
+#define QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG_M              GENMASK(13, 11)
+#define QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG_X(x)           (((x) & GENMASK(13, 11)) >> 11)
+#define QSYS_SWITCH_PORT_MODE_YEL_RSRVD                   BIT(10)
+#define QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE           BIT(9)
+#define QSYS_SWITCH_PORT_MODE_TX_PFC_ENA(x)               (((x) << 1) & GENMASK(8, 1))
+#define QSYS_SWITCH_PORT_MODE_TX_PFC_ENA_M                GENMASK(8, 1)
+#define QSYS_SWITCH_PORT_MODE_TX_PFC_ENA_X(x)             (((x) & GENMASK(8, 1)) >> 1)
+#define QSYS_SWITCH_PORT_MODE_TX_PFC_MODE                 BIT(0)
+
+#define QSYS_STAT_CNT_CFG_TX_GREEN_CNT_MODE               BIT(5)
+#define QSYS_STAT_CNT_CFG_TX_YELLOW_CNT_MODE              BIT(4)
+#define QSYS_STAT_CNT_CFG_DROP_GREEN_CNT_MODE             BIT(3)
+#define QSYS_STAT_CNT_CFG_DROP_YELLOW_CNT_MODE            BIT(2)
+#define QSYS_STAT_CNT_CFG_DROP_COUNT_ONCE                 BIT(1)
+#define QSYS_STAT_CNT_CFG_DROP_COUNT_EGRESS               BIT(0)
+
+#define QSYS_EEE_CFG_RSZ                                  0x4
+
+#define QSYS_EEE_THRES_EEE_HIGH_BYTES(x)                  (((x) << 8) & GENMASK(15, 8))
+#define QSYS_EEE_THRES_EEE_HIGH_BYTES_M                   GENMASK(15, 8)
+#define QSYS_EEE_THRES_EEE_HIGH_BYTES_X(x)                (((x) & GENMASK(15, 8)) >> 8)
+#define QSYS_EEE_THRES_EEE_HIGH_FRAMES(x)                 ((x) & GENMASK(7, 0))
+#define QSYS_EEE_THRES_EEE_HIGH_FRAMES_M                  GENMASK(7, 0)
+
+#define QSYS_SW_STATUS_RSZ                                0x4
+
+#define QSYS_EXT_CPU_CFG_EXT_CPU_PORT(x)                  (((x) << 8) & GENMASK(12, 8))
+#define QSYS_EXT_CPU_CFG_EXT_CPU_PORT_M                   GENMASK(12, 8)
+#define QSYS_EXT_CPU_CFG_EXT_CPU_PORT_X(x)                (((x) & GENMASK(12, 8)) >> 8)
+#define QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK(x)                  ((x) & GENMASK(7, 0))
+#define QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M                   GENMASK(7, 0)
+
+#define QSYS_QMAP_GSZ                                     0x4
+
+#define QSYS_QMAP_SE_BASE(x)                              (((x) << 5) & GENMASK(12, 5))
+#define QSYS_QMAP_SE_BASE_M                               GENMASK(12, 5)
+#define QSYS_QMAP_SE_BASE_X(x)                            (((x) & GENMASK(12, 5)) >> 5)
+#define QSYS_QMAP_SE_IDX_SEL(x)                           (((x) << 2) & GENMASK(4, 2))
+#define QSYS_QMAP_SE_IDX_SEL_M                            GENMASK(4, 2)
+#define QSYS_QMAP_SE_IDX_SEL_X(x)                         (((x) & GENMASK(4, 2)) >> 2)
+#define QSYS_QMAP_SE_INP_SEL(x)                           ((x) & GENMASK(1, 0))
+#define QSYS_QMAP_SE_INP_SEL_M                            GENMASK(1, 0)
+
+#define QSYS_ISDX_SGRP_GSZ                                0x4
+
+#define QSYS_TIMED_FRAME_ENTRY_GSZ                        0x4
+
+#define QSYS_TFRM_MISC_TIMED_CANCEL_SLOT(x)               (((x) << 9) & GENMASK(18, 9))
+#define QSYS_TFRM_MISC_TIMED_CANCEL_SLOT_M                GENMASK(18, 9)
+#define QSYS_TFRM_MISC_TIMED_CANCEL_SLOT_X(x)             (((x) & GENMASK(18, 9)) >> 9)
+#define QSYS_TFRM_MISC_TIMED_CANCEL_1SHOT                 BIT(8)
+#define QSYS_TFRM_MISC_TIMED_SLOT_MODE_MC                 BIT(7)
+#define QSYS_TFRM_MISC_TIMED_ENTRY_FAST_CNT(x)            ((x) & GENMASK(6, 0))
+#define QSYS_TFRM_MISC_TIMED_ENTRY_FAST_CNT_M             GENMASK(6, 0)
+
+#define QSYS_RED_PROFILE_RSZ                              0x4
+
+#define QSYS_RED_PROFILE_WM_RED_LOW(x)                    (((x) << 8) & GENMASK(15, 8))
+#define QSYS_RED_PROFILE_WM_RED_LOW_M                     GENMASK(15, 8)
+#define QSYS_RED_PROFILE_WM_RED_LOW_X(x)                  (((x) & GENMASK(15, 8)) >> 8)
+#define QSYS_RED_PROFILE_WM_RED_HIGH(x)                   ((x) & GENMASK(7, 0))
+#define QSYS_RED_PROFILE_WM_RED_HIGH_M                    GENMASK(7, 0)
+
+#define QSYS_RES_CFG_GSZ                                  0x8
+
+#define QSYS_RES_STAT_GSZ                                 0x8
+
+#define QSYS_RES_STAT_INUSE(x)                            (((x) << 12) & GENMASK(23, 12))
+#define QSYS_RES_STAT_INUSE_M                             GENMASK(23, 12)
+#define QSYS_RES_STAT_INUSE_X(x)                          (((x) & GENMASK(23, 12)) >> 12)
+#define QSYS_RES_STAT_MAXUSE(x)                           ((x) & GENMASK(11, 0))
+#define QSYS_RES_STAT_MAXUSE_M                            GENMASK(11, 0)
+
+#define QSYS_EVENTS_CORE_EV_FDC(x)                        (((x) << 2) & GENMASK(4, 2))
+#define QSYS_EVENTS_CORE_EV_FDC_M                         GENMASK(4, 2)
+#define QSYS_EVENTS_CORE_EV_FDC_X(x)                      (((x) & GENMASK(4, 2)) >> 2)
+#define QSYS_EVENTS_CORE_EV_FRD(x)                        ((x) & GENMASK(1, 0))
+#define QSYS_EVENTS_CORE_EV_FRD_M                         GENMASK(1, 0)
+
+#define QSYS_QMAXSDU_CFG_0_RSZ                            0x4
+
+#define QSYS_QMAXSDU_CFG_1_RSZ                            0x4
+
+#define QSYS_QMAXSDU_CFG_2_RSZ                            0x4
+
+#define QSYS_QMAXSDU_CFG_3_RSZ                            0x4
+
+#define QSYS_QMAXSDU_CFG_4_RSZ                            0x4
+
+#define QSYS_QMAXSDU_CFG_5_RSZ                            0x4
+
+#define QSYS_QMAXSDU_CFG_6_RSZ                            0x4
+
+#define QSYS_QMAXSDU_CFG_7_RSZ                            0x4
+
+#define QSYS_PREEMPTION_CFG_RSZ                           0x4
+
+#define QSYS_PREEMPTION_CFG_P_QUEUES(x)                   ((x) & GENMASK(7, 0))
+#define QSYS_PREEMPTION_CFG_P_QUEUES_M                    GENMASK(7, 0)
+#define QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(x)           (((x) << 8) & GENMASK(9, 8))
+#define QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M            GENMASK(9, 8)
+#define QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(x)         (((x) & GENMASK(9, 8)) >> 8)
+#define QSYS_PREEMPTION_CFG_STRICT_IPG(x)                 (((x) << 12) & GENMASK(13, 12))
+#define QSYS_PREEMPTION_CFG_STRICT_IPG_M                  GENMASK(13, 12)
+#define QSYS_PREEMPTION_CFG_STRICT_IPG_X(x)               (((x) & GENMASK(13, 12)) >> 12)
+#define QSYS_PREEMPTION_CFG_HOLD_ADVANCE(x)               (((x) << 16) & GENMASK(31, 16))
+#define QSYS_PREEMPTION_CFG_HOLD_ADVANCE_M                GENMASK(31, 16)
+#define QSYS_PREEMPTION_CFG_HOLD_ADVANCE_X(x)             (((x) & GENMASK(31, 16)) >> 16)
+
+#define QSYS_CIR_CFG_GSZ                                  0x80
+
+#define QSYS_CIR_CFG_CIR_RATE(x)                          (((x) << 6) & GENMASK(20, 6))
+#define QSYS_CIR_CFG_CIR_RATE_M                           GENMASK(20, 6)
+#define QSYS_CIR_CFG_CIR_RATE_X(x)                        (((x) & GENMASK(20, 6)) >> 6)
+#define QSYS_CIR_CFG_CIR_BURST(x)                         ((x) & GENMASK(5, 0))
+#define QSYS_CIR_CFG_CIR_BURST_M                          GENMASK(5, 0)
+
+#define QSYS_EIR_CFG_GSZ                                  0x80
+
+#define QSYS_EIR_CFG_EIR_RATE(x)                          (((x) << 7) & GENMASK(21, 7))
+#define QSYS_EIR_CFG_EIR_RATE_M                           GENMASK(21, 7)
+#define QSYS_EIR_CFG_EIR_RATE_X(x)                        (((x) & GENMASK(21, 7)) >> 7)
+#define QSYS_EIR_CFG_EIR_BURST(x)                         (((x) << 1) & GENMASK(6, 1))
+#define QSYS_EIR_CFG_EIR_BURST_M                          GENMASK(6, 1)
+#define QSYS_EIR_CFG_EIR_BURST_X(x)                       (((x) & GENMASK(6, 1)) >> 1)
+#define QSYS_EIR_CFG_EIR_MARK_ENA                         BIT(0)
+
+#define QSYS_SE_CFG_GSZ                                   0x80
+
+#define QSYS_SE_CFG_SE_DWRR_CNT(x)                        (((x) << 6) & GENMASK(9, 6))
+#define QSYS_SE_CFG_SE_DWRR_CNT_M                         GENMASK(9, 6)
+#define QSYS_SE_CFG_SE_DWRR_CNT_X(x)                      (((x) & GENMASK(9, 6)) >> 6)
+#define QSYS_SE_CFG_SE_RR_ENA                             BIT(5)
+#define QSYS_SE_CFG_SE_AVB_ENA                            BIT(4)
+#define QSYS_SE_CFG_SE_FRM_MODE(x)                        (((x) << 2) & GENMASK(3, 2))
+#define QSYS_SE_CFG_SE_FRM_MODE_M                         GENMASK(3, 2)
+#define QSYS_SE_CFG_SE_FRM_MODE_X(x)                      (((x) & GENMASK(3, 2)) >> 2)
+#define QSYS_SE_CFG_SE_EXC_ENA                            BIT(1)
+#define QSYS_SE_CFG_SE_EXC_FWD                            BIT(0)
+
+#define QSYS_SE_DWRR_CFG_GSZ                              0x80
+#define QSYS_SE_DWRR_CFG_RSZ                              0x4
+
+#define QSYS_SE_CONNECT_GSZ                               0x80
+
+#define QSYS_SE_CONNECT_SE_OUTP_IDX(x)                    (((x) << 17) & GENMASK(24, 17))
+#define QSYS_SE_CONNECT_SE_OUTP_IDX_M                     GENMASK(24, 17)
+#define QSYS_SE_CONNECT_SE_OUTP_IDX_X(x)                  (((x) & GENMASK(24, 17)) >> 17)
+#define QSYS_SE_CONNECT_SE_INP_IDX(x)                     (((x) << 9) & GENMASK(16, 9))
+#define QSYS_SE_CONNECT_SE_INP_IDX_M                      GENMASK(16, 9)
+#define QSYS_SE_CONNECT_SE_INP_IDX_X(x)                   (((x) & GENMASK(16, 9)) >> 9)
+#define QSYS_SE_CONNECT_SE_OUTP_CON(x)                    (((x) << 5) & GENMASK(8, 5))
+#define QSYS_SE_CONNECT_SE_OUTP_CON_M                     GENMASK(8, 5)
+#define QSYS_SE_CONNECT_SE_OUTP_CON_X(x)                  (((x) & GENMASK(8, 5)) >> 5)
+#define QSYS_SE_CONNECT_SE_INP_CNT(x)                     (((x) << 1) & GENMASK(4, 1))
+#define QSYS_SE_CONNECT_SE_INP_CNT_M                      GENMASK(4, 1)
+#define QSYS_SE_CONNECT_SE_INP_CNT_X(x)                   (((x) & GENMASK(4, 1)) >> 1)
+#define QSYS_SE_CONNECT_SE_TERMINAL                       BIT(0)
+
+#define QSYS_SE_DLB_SENSE_GSZ                             0x80
+
+#define QSYS_SE_DLB_SENSE_SE_DLB_PRIO(x)                  (((x) << 11) & GENMASK(13, 11))
+#define QSYS_SE_DLB_SENSE_SE_DLB_PRIO_M                   GENMASK(13, 11)
+#define QSYS_SE_DLB_SENSE_SE_DLB_PRIO_X(x)                (((x) & GENMASK(13, 11)) >> 11)
+#define QSYS_SE_DLB_SENSE_SE_DLB_SPORT(x)                 (((x) << 7) & GENMASK(10, 7))
+#define QSYS_SE_DLB_SENSE_SE_DLB_SPORT_M                  GENMASK(10, 7)
+#define QSYS_SE_DLB_SENSE_SE_DLB_SPORT_X(x)               (((x) & GENMASK(10, 7)) >> 7)
+#define QSYS_SE_DLB_SENSE_SE_DLB_DPORT(x)                 (((x) << 3) & GENMASK(6, 3))
+#define QSYS_SE_DLB_SENSE_SE_DLB_DPORT_M                  GENMASK(6, 3)
+#define QSYS_SE_DLB_SENSE_SE_DLB_DPORT_X(x)               (((x) & GENMASK(6, 3)) >> 3)
+#define QSYS_SE_DLB_SENSE_SE_DLB_PRIO_ENA                 BIT(2)
+#define QSYS_SE_DLB_SENSE_SE_DLB_SPORT_ENA                BIT(1)
+#define QSYS_SE_DLB_SENSE_SE_DLB_DPORT_ENA                BIT(0)
+
+#define QSYS_CIR_STATE_GSZ                                0x80
+
+#define QSYS_CIR_STATE_CIR_LVL(x)                         (((x) << 4) & GENMASK(25, 4))
+#define QSYS_CIR_STATE_CIR_LVL_M                          GENMASK(25, 4)
+#define QSYS_CIR_STATE_CIR_LVL_X(x)                       (((x) & GENMASK(25, 4)) >> 4)
+#define QSYS_CIR_STATE_SHP_TIME(x)                        ((x) & GENMASK(3, 0))
+#define QSYS_CIR_STATE_SHP_TIME_M                         GENMASK(3, 0)
+
+#define QSYS_EIR_STATE_GSZ                                0x80
+
+#define QSYS_SE_STATE_GSZ                                 0x80
+
+#define QSYS_SE_STATE_SE_OUTP_LVL(x)                      (((x) << 1) & GENMASK(2, 1))
+#define QSYS_SE_STATE_SE_OUTP_LVL_M                       GENMASK(2, 1)
+#define QSYS_SE_STATE_SE_OUTP_LVL_X(x)                    (((x) & GENMASK(2, 1)) >> 1)
+#define QSYS_SE_STATE_SE_WAS_YEL                          BIT(0)
+
+#define QSYS_HSCH_MISC_CFG_SE_CONNECT_VLD                 BIT(8)
+#define QSYS_HSCH_MISC_CFG_FRM_ADJ(x)                     (((x) << 3) & GENMASK(7, 3))
+#define QSYS_HSCH_MISC_CFG_FRM_ADJ_M                      GENMASK(7, 3)
+#define QSYS_HSCH_MISC_CFG_FRM_ADJ_X(x)                   (((x) & GENMASK(7, 3)) >> 3)
+#define QSYS_HSCH_MISC_CFG_LEAK_DIS                       BIT(2)
+#define QSYS_HSCH_MISC_CFG_QSHP_EXC_ENA                   BIT(1)
+#define QSYS_HSCH_MISC_CFG_PFC_BYP_UPD                    BIT(0)
+
+#define QSYS_TAG_CONFIG_RSZ                               0x4
+
+#define QSYS_TAG_CONFIG_ENABLE                            BIT(0)
+#define QSYS_TAG_CONFIG_LINK_SPEED(x)                     (((x) << 4) & GENMASK(5, 4))
+#define QSYS_TAG_CONFIG_LINK_SPEED_M                      GENMASK(5, 4)
+#define QSYS_TAG_CONFIG_LINK_SPEED_X(x)                   (((x) & GENMASK(5, 4)) >> 4)
+#define QSYS_TAG_CONFIG_INIT_GATE_STATE(x)                (((x) << 8) & GENMASK(15, 8))
+#define QSYS_TAG_CONFIG_INIT_GATE_STATE_M                 GENMASK(15, 8)
+#define QSYS_TAG_CONFIG_INIT_GATE_STATE_X(x)              (((x) & GENMASK(15, 8)) >> 8)
+#define QSYS_TAG_CONFIG_SCH_TRAFFIC_QUEUES(x)             (((x) << 16) & GENMASK(23, 16))
+#define QSYS_TAG_CONFIG_SCH_TRAFFIC_QUEUES_M              GENMASK(23, 16)
+#define QSYS_TAG_CONFIG_SCH_TRAFFIC_QUEUES_X(x)           (((x) & GENMASK(23, 16)) >> 16)
+
+#define QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM(x)               ((x) & GENMASK(7, 0))
+#define QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM_M                GENMASK(7, 0)
+#define QSYS_TAS_PARAM_CFG_CTRL_ALWAYS_GUARD_BAND_SCH_Q   BIT(8)
+#define QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE             BIT(16)
+
+#define QSYS_PORT_MAX_SDU_RSZ                             0x4
+
+#define QSYS_PARAM_CFG_REG_3_BASE_TIME_SEC_MSB(x)         ((x) & GENMASK(15, 0))
+#define QSYS_PARAM_CFG_REG_3_BASE_TIME_SEC_MSB_M          GENMASK(15, 0)
+#define QSYS_PARAM_CFG_REG_3_LIST_LENGTH(x)               (((x) << 16) & GENMASK(31, 16))
+#define QSYS_PARAM_CFG_REG_3_LIST_LENGTH_M                GENMASK(31, 16)
+#define QSYS_PARAM_CFG_REG_3_LIST_LENGTH_X(x)             (((x) & GENMASK(31, 16)) >> 16)
+
+#define QSYS_GCL_CFG_REG_1_GCL_ENTRY_NUM(x)               ((x) & GENMASK(5, 0))
+#define QSYS_GCL_CFG_REG_1_GCL_ENTRY_NUM_M                GENMASK(5, 0)
+#define QSYS_GCL_CFG_REG_1_GATE_STATE(x)                  (((x) << 8) & GENMASK(15, 8))
+#define QSYS_GCL_CFG_REG_1_GATE_STATE_M                   GENMASK(15, 8)
+#define QSYS_GCL_CFG_REG_1_GATE_STATE_X(x)                (((x) & GENMASK(15, 8)) >> 8)
+
+#define QSYS_PARAM_STATUS_REG_3_BASE_TIME_SEC_MSB(x)      ((x) & GENMASK(15, 0))
+#define QSYS_PARAM_STATUS_REG_3_BASE_TIME_SEC_MSB_M       GENMASK(15, 0)
+#define QSYS_PARAM_STATUS_REG_3_LIST_LENGTH(x)            (((x) << 16) & GENMASK(31, 16))
+#define QSYS_PARAM_STATUS_REG_3_LIST_LENGTH_M             GENMASK(31, 16)
+#define QSYS_PARAM_STATUS_REG_3_LIST_LENGTH_X(x)          (((x) & GENMASK(31, 16)) >> 16)
+
+#define QSYS_PARAM_STATUS_REG_8_CFG_CHG_TIME_SEC_MSB(x)   ((x) & GENMASK(15, 0))
+#define QSYS_PARAM_STATUS_REG_8_CFG_CHG_TIME_SEC_MSB_M    GENMASK(15, 0)
+#define QSYS_PARAM_STATUS_REG_8_OPER_GATE_STATE(x)        (((x) << 16) & GENMASK(23, 16))
+#define QSYS_PARAM_STATUS_REG_8_OPER_GATE_STATE_M         GENMASK(23, 16)
+#define QSYS_PARAM_STATUS_REG_8_OPER_GATE_STATE_X(x)      (((x) & GENMASK(23, 16)) >> 16)
+#define QSYS_PARAM_STATUS_REG_8_CONFIG_PENDING            BIT(24)
+
+#define QSYS_GCL_STATUS_REG_1_GCL_ENTRY_NUM(x)            ((x) & GENMASK(5, 0))
+#define QSYS_GCL_STATUS_REG_1_GCL_ENTRY_NUM_M             GENMASK(5, 0)
+#define QSYS_GCL_STATUS_REG_1_GATE_STATE(x)               (((x) << 8) & GENMASK(15, 8))
+#define QSYS_GCL_STATUS_REG_1_GATE_STATE_M                GENMASK(15, 8)
+#define QSYS_GCL_STATUS_REG_1_GATE_STATE_X(x)             (((x) & GENMASK(15, 8)) >> 8)
+
+#endif
diff --git a/drivers/net/ethernet/mscc/ocelot_regs.c b/drivers/net/ethernet/mscc/ocelot_regs.c
new file mode 100644 (file)
index 0000000..e334b40
--- /dev/null
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+#include "ocelot.h"
+
+static const u32 ocelot_ana_regmap[] = {
+       REG(ANA_ADVLEARN,                  0x009000),
+       REG(ANA_VLANMASK,                  0x009004),
+       REG(ANA_PORT_B_DOMAIN,             0x009008),
+       REG(ANA_ANAGEFIL,                  0x00900c),
+       REG(ANA_ANEVENTS,                  0x009010),
+       REG(ANA_STORMLIMIT_BURST,          0x009014),
+       REG(ANA_STORMLIMIT_CFG,            0x009018),
+       REG(ANA_ISOLATED_PORTS,            0x009028),
+       REG(ANA_COMMUNITY_PORTS,           0x00902c),
+       REG(ANA_AUTOAGE,                   0x009030),
+       REG(ANA_MACTOPTIONS,               0x009034),
+       REG(ANA_LEARNDISC,                 0x009038),
+       REG(ANA_AGENCTRL,                  0x00903c),
+       REG(ANA_MIRRORPORTS,               0x009040),
+       REG(ANA_EMIRRORPORTS,              0x009044),
+       REG(ANA_FLOODING,                  0x009048),
+       REG(ANA_FLOODING_IPMC,             0x00904c),
+       REG(ANA_SFLOW_CFG,                 0x009050),
+       REG(ANA_PORT_MODE,                 0x009080),
+       REG(ANA_PGID_PGID,                 0x008c00),
+       REG(ANA_TABLES_ANMOVED,            0x008b30),
+       REG(ANA_TABLES_MACHDATA,           0x008b34),
+       REG(ANA_TABLES_MACLDATA,           0x008b38),
+       REG(ANA_TABLES_MACACCESS,          0x008b3c),
+       REG(ANA_TABLES_MACTINDX,           0x008b40),
+       REG(ANA_TABLES_VLANACCESS,         0x008b44),
+       REG(ANA_TABLES_VLANTIDX,           0x008b48),
+       REG(ANA_TABLES_ISDXACCESS,         0x008b4c),
+       REG(ANA_TABLES_ISDXTIDX,           0x008b50),
+       REG(ANA_TABLES_ENTRYLIM,           0x008b00),
+       REG(ANA_TABLES_PTP_ID_HIGH,        0x008b54),
+       REG(ANA_TABLES_PTP_ID_LOW,         0x008b58),
+       REG(ANA_MSTI_STATE,                0x008e00),
+       REG(ANA_PORT_VLAN_CFG,             0x007000),
+       REG(ANA_PORT_DROP_CFG,             0x007004),
+       REG(ANA_PORT_QOS_CFG,              0x007008),
+       REG(ANA_PORT_VCAP_CFG,             0x00700c),
+       REG(ANA_PORT_VCAP_S1_KEY_CFG,      0x007010),
+       REG(ANA_PORT_VCAP_S2_CFG,          0x00701c),
+       REG(ANA_PORT_PCP_DEI_MAP,          0x007020),
+       REG(ANA_PORT_CPU_FWD_CFG,          0x007060),
+       REG(ANA_PORT_CPU_FWD_BPDU_CFG,     0x007064),
+       REG(ANA_PORT_CPU_FWD_GARP_CFG,     0x007068),
+       REG(ANA_PORT_CPU_FWD_CCM_CFG,      0x00706c),
+       REG(ANA_PORT_PORT_CFG,             0x007070),
+       REG(ANA_PORT_POL_CFG,              0x007074),
+       REG(ANA_PORT_PTP_CFG,              0x007078),
+       REG(ANA_PORT_PTP_DLY1_CFG,         0x00707c),
+       REG(ANA_OAM_UPM_LM_CNT,            0x007c00),
+       REG(ANA_PORT_PTP_DLY2_CFG,         0x007080),
+       REG(ANA_PFC_PFC_CFG,               0x008800),
+       REG(ANA_PFC_PFC_TIMER,             0x008804),
+       REG(ANA_IPT_OAM_MEP_CFG,           0x008000),
+       REG(ANA_IPT_IPT,                   0x008004),
+       REG(ANA_PPT_PPT,                   0x008ac0),
+       REG(ANA_FID_MAP_FID_MAP,           0x000000),
+       REG(ANA_AGGR_CFG,                  0x0090b4),
+       REG(ANA_CPUQ_CFG,                  0x0090b8),
+       REG(ANA_CPUQ_CFG2,                 0x0090bc),
+       REG(ANA_CPUQ_8021_CFG,             0x0090c0),
+       REG(ANA_DSCP_CFG,                  0x009100),
+       REG(ANA_DSCP_REWR_CFG,             0x009200),
+       REG(ANA_VCAP_RNG_TYPE_CFG,         0x009240),
+       REG(ANA_VCAP_RNG_VAL_CFG,          0x009260),
+       REG(ANA_VRAP_CFG,                  0x009280),
+       REG(ANA_VRAP_HDR_DATA,             0x009284),
+       REG(ANA_VRAP_HDR_MASK,             0x009288),
+       REG(ANA_DISCARD_CFG,               0x00928c),
+       REG(ANA_FID_CFG,                   0x009290),
+       REG(ANA_POL_PIR_CFG,               0x004000),
+       REG(ANA_POL_CIR_CFG,               0x004004),
+       REG(ANA_POL_MODE_CFG,              0x004008),
+       REG(ANA_POL_PIR_STATE,             0x00400c),
+       REG(ANA_POL_CIR_STATE,             0x004010),
+       REG(ANA_POL_STATE,                 0x004014),
+       REG(ANA_POL_FLOWC,                 0x008b80),
+       REG(ANA_POL_HYST,                  0x008bec),
+       REG(ANA_POL_MISC_CFG,              0x008bf0),
+};
+
+static const u32 ocelot_qs_regmap[] = {
+       REG(QS_XTR_GRP_CFG,                0x000000),
+       REG(QS_XTR_RD,                     0x000008),
+       REG(QS_XTR_FRM_PRUNING,            0x000010),
+       REG(QS_XTR_FLUSH,                  0x000018),
+       REG(QS_XTR_DATA_PRESENT,           0x00001c),
+       REG(QS_XTR_CFG,                    0x000020),
+       REG(QS_INJ_GRP_CFG,                0x000024),
+       REG(QS_INJ_WR,                     0x00002c),
+       REG(QS_INJ_CTRL,                   0x000034),
+       REG(QS_INJ_STATUS,                 0x00003c),
+       REG(QS_INJ_ERR,                    0x000040),
+       REG(QS_INH_DBG,                    0x000048),
+};
+
+static const u32 ocelot_hsio_regmap[] = {
+       REG(HSIO_PLL5G_CFG0,               0x000000),
+       REG(HSIO_PLL5G_CFG1,               0x000004),
+       REG(HSIO_PLL5G_CFG2,               0x000008),
+       REG(HSIO_PLL5G_CFG3,               0x00000c),
+       REG(HSIO_PLL5G_CFG4,               0x000010),
+       REG(HSIO_PLL5G_CFG5,               0x000014),
+       REG(HSIO_PLL5G_CFG6,               0x000018),
+       REG(HSIO_PLL5G_STATUS0,            0x00001c),
+       REG(HSIO_PLL5G_STATUS1,            0x000020),
+       REG(HSIO_PLL5G_BIST_CFG0,          0x000024),
+       REG(HSIO_PLL5G_BIST_CFG1,          0x000028),
+       REG(HSIO_PLL5G_BIST_CFG2,          0x00002c),
+       REG(HSIO_PLL5G_BIST_STAT0,         0x000030),
+       REG(HSIO_PLL5G_BIST_STAT1,         0x000034),
+       REG(HSIO_RCOMP_CFG0,               0x000038),
+       REG(HSIO_RCOMP_STATUS,             0x00003c),
+       REG(HSIO_SYNC_ETH_CFG,             0x000040),
+       REG(HSIO_SYNC_ETH_PLL_CFG,         0x000048),
+       REG(HSIO_S1G_DES_CFG,              0x00004c),
+       REG(HSIO_S1G_IB_CFG,               0x000050),
+       REG(HSIO_S1G_OB_CFG,               0x000054),
+       REG(HSIO_S1G_SER_CFG,              0x000058),
+       REG(HSIO_S1G_COMMON_CFG,           0x00005c),
+       REG(HSIO_S1G_PLL_CFG,              0x000060),
+       REG(HSIO_S1G_PLL_STATUS,           0x000064),
+       REG(HSIO_S1G_DFT_CFG0,             0x000068),
+       REG(HSIO_S1G_DFT_CFG1,             0x00006c),
+       REG(HSIO_S1G_DFT_CFG2,             0x000070),
+       REG(HSIO_S1G_TP_CFG,               0x000074),
+       REG(HSIO_S1G_RC_PLL_BIST_CFG,      0x000078),
+       REG(HSIO_S1G_MISC_CFG,             0x00007c),
+       REG(HSIO_S1G_DFT_STATUS,           0x000080),
+       REG(HSIO_S1G_MISC_STATUS,          0x000084),
+       REG(HSIO_MCB_S1G_ADDR_CFG,         0x000088),
+       REG(HSIO_S6G_DIG_CFG,              0x00008c),
+       REG(HSIO_S6G_DFT_CFG0,             0x000090),
+       REG(HSIO_S6G_DFT_CFG1,             0x000094),
+       REG(HSIO_S6G_DFT_CFG2,             0x000098),
+       REG(HSIO_S6G_TP_CFG0,              0x00009c),
+       REG(HSIO_S6G_TP_CFG1,              0x0000a0),
+       REG(HSIO_S6G_RC_PLL_BIST_CFG,      0x0000a4),
+       REG(HSIO_S6G_MISC_CFG,             0x0000a8),
+       REG(HSIO_S6G_OB_ANEG_CFG,          0x0000ac),
+       REG(HSIO_S6G_DFT_STATUS,           0x0000b0),
+       REG(HSIO_S6G_ERR_CNT,              0x0000b4),
+       REG(HSIO_S6G_MISC_STATUS,          0x0000b8),
+       REG(HSIO_S6G_DES_CFG,              0x0000bc),
+       REG(HSIO_S6G_IB_CFG,               0x0000c0),
+       REG(HSIO_S6G_IB_CFG1,              0x0000c4),
+       REG(HSIO_S6G_IB_CFG2,              0x0000c8),
+       REG(HSIO_S6G_IB_CFG3,              0x0000cc),
+       REG(HSIO_S6G_IB_CFG4,              0x0000d0),
+       REG(HSIO_S6G_IB_CFG5,              0x0000d4),
+       REG(HSIO_S6G_OB_CFG,               0x0000d8),
+       REG(HSIO_S6G_OB_CFG1,              0x0000dc),
+       REG(HSIO_S6G_SER_CFG,              0x0000e0),
+       REG(HSIO_S6G_COMMON_CFG,           0x0000e4),
+       REG(HSIO_S6G_PLL_CFG,              0x0000e8),
+       REG(HSIO_S6G_ACJTAG_CFG,           0x0000ec),
+       REG(HSIO_S6G_GP_CFG,               0x0000f0),
+       REG(HSIO_S6G_IB_STATUS0,           0x0000f4),
+       REG(HSIO_S6G_IB_STATUS1,           0x0000f8),
+       REG(HSIO_S6G_ACJTAG_STATUS,        0x0000fc),
+       REG(HSIO_S6G_PLL_STATUS,           0x000100),
+       REG(HSIO_S6G_REVID,                0x000104),
+       REG(HSIO_MCB_S6G_ADDR_CFG,         0x000108),
+       REG(HSIO_HW_CFG,                   0x00010c),
+       REG(HSIO_HW_QSGMII_CFG,            0x000110),
+       REG(HSIO_HW_QSGMII_STAT,           0x000114),
+       REG(HSIO_CLK_CFG,                  0x000118),
+       REG(HSIO_TEMP_SENSOR_CTRL,         0x00011c),
+       REG(HSIO_TEMP_SENSOR_CFG,          0x000120),
+       REG(HSIO_TEMP_SENSOR_STAT,         0x000124),
+};
+
+static const u32 ocelot_qsys_regmap[] = {
+       REG(QSYS_PORT_MODE,                0x011200),
+       REG(QSYS_SWITCH_PORT_MODE,         0x011234),
+       REG(QSYS_STAT_CNT_CFG,             0x011264),
+       REG(QSYS_EEE_CFG,                  0x011268),
+       REG(QSYS_EEE_THRES,                0x011294),
+       REG(QSYS_IGR_NO_SHARING,           0x011298),
+       REG(QSYS_EGR_NO_SHARING,           0x01129c),
+       REG(QSYS_SW_STATUS,                0x0112a0),
+       REG(QSYS_EXT_CPU_CFG,              0x0112d0),
+       REG(QSYS_PAD_CFG,                  0x0112d4),
+       REG(QSYS_CPU_GROUP_MAP,            0x0112d8),
+       REG(QSYS_QMAP,                     0x0112dc),
+       REG(QSYS_ISDX_SGRP,                0x011400),
+       REG(QSYS_TIMED_FRAME_ENTRY,        0x014000),
+       REG(QSYS_TFRM_MISC,                0x011310),
+       REG(QSYS_TFRM_PORT_DLY,            0x011314),
+       REG(QSYS_TFRM_TIMER_CFG_1,         0x011318),
+       REG(QSYS_TFRM_TIMER_CFG_2,         0x01131c),
+       REG(QSYS_TFRM_TIMER_CFG_3,         0x011320),
+       REG(QSYS_TFRM_TIMER_CFG_4,         0x011324),
+       REG(QSYS_TFRM_TIMER_CFG_5,         0x011328),
+       REG(QSYS_TFRM_TIMER_CFG_6,         0x01132c),
+       REG(QSYS_TFRM_TIMER_CFG_7,         0x011330),
+       REG(QSYS_TFRM_TIMER_CFG_8,         0x011334),
+       REG(QSYS_RED_PROFILE,              0x011338),
+       REG(QSYS_RES_QOS_MODE,             0x011378),
+       REG(QSYS_RES_CFG,                  0x012000),
+       REG(QSYS_RES_STAT,                 0x012004),
+       REG(QSYS_EGR_DROP_MODE,            0x01137c),
+       REG(QSYS_EQ_CTRL,                  0x011380),
+       REG(QSYS_EVENTS_CORE,              0x011384),
+       REG(QSYS_CIR_CFG,                  0x000000),
+       REG(QSYS_EIR_CFG,                  0x000004),
+       REG(QSYS_SE_CFG,                   0x000008),
+       REG(QSYS_SE_DWRR_CFG,              0x00000c),
+       REG(QSYS_SE_CONNECT,               0x00003c),
+       REG(QSYS_SE_DLB_SENSE,             0x000040),
+       REG(QSYS_CIR_STATE,                0x000044),
+       REG(QSYS_EIR_STATE,                0x000048),
+       REG(QSYS_SE_STATE,                 0x00004c),
+       REG(QSYS_HSCH_MISC_CFG,            0x011388),
+};
+
+static const u32 ocelot_rew_regmap[] = {
+       REG(REW_PORT_VLAN_CFG,             0x000000),
+       REG(REW_TAG_CFG,                   0x000004),
+       REG(REW_PORT_CFG,                  0x000008),
+       REG(REW_DSCP_CFG,                  0x00000c),
+       REG(REW_PCP_DEI_QOS_MAP_CFG,       0x000010),
+       REG(REW_PTP_CFG,                   0x000050),
+       REG(REW_PTP_DLY1_CFG,              0x000054),
+       REG(REW_DSCP_REMAP_DP1_CFG,        0x000690),
+       REG(REW_DSCP_REMAP_CFG,            0x000790),
+       REG(REW_STAT_CFG,                  0x000890),
+       REG(REW_PPT,                       0x000680),
+};
+
+static const u32 ocelot_sys_regmap[] = {
+       REG(SYS_COUNT_RX_OCTETS,           0x000000),
+       REG(SYS_COUNT_RX_UNICAST,          0x000004),
+       REG(SYS_COUNT_RX_MULTICAST,        0x000008),
+       REG(SYS_COUNT_RX_BROADCAST,        0x00000c),
+       REG(SYS_COUNT_RX_SHORTS,           0x000010),
+       REG(SYS_COUNT_RX_FRAGMENTS,        0x000014),
+       REG(SYS_COUNT_RX_JABBERS,          0x000018),
+       REG(SYS_COUNT_RX_CRC_ALIGN_ERRS,   0x00001c),
+       REG(SYS_COUNT_RX_SYM_ERRS,         0x000020),
+       REG(SYS_COUNT_RX_64,               0x000024),
+       REG(SYS_COUNT_RX_65_127,           0x000028),
+       REG(SYS_COUNT_RX_128_255,          0x00002c),
+       REG(SYS_COUNT_RX_256_1023,         0x000030),
+       REG(SYS_COUNT_RX_1024_1526,        0x000034),
+       REG(SYS_COUNT_RX_1527_MAX,         0x000038),
+       REG(SYS_COUNT_RX_PAUSE,            0x00003c),
+       REG(SYS_COUNT_RX_CONTROL,          0x000040),
+       REG(SYS_COUNT_RX_LONGS,            0x000044),
+       REG(SYS_COUNT_RX_CLASSIFIED_DROPS, 0x000048),
+       REG(SYS_COUNT_TX_OCTETS,           0x000100),
+       REG(SYS_COUNT_TX_UNICAST,          0x000104),
+       REG(SYS_COUNT_TX_MULTICAST,        0x000108),
+       REG(SYS_COUNT_TX_BROADCAST,        0x00010c),
+       REG(SYS_COUNT_TX_COLLISION,        0x000110),
+       REG(SYS_COUNT_TX_DROPS,            0x000114),
+       REG(SYS_COUNT_TX_PAUSE,            0x000118),
+       REG(SYS_COUNT_TX_64,               0x00011c),
+       REG(SYS_COUNT_TX_65_127,           0x000120),
+       REG(SYS_COUNT_TX_128_511,          0x000124),
+       REG(SYS_COUNT_TX_512_1023,         0x000128),
+       REG(SYS_COUNT_TX_1024_1526,        0x00012c),
+       REG(SYS_COUNT_TX_1527_MAX,         0x000130),
+       REG(SYS_COUNT_TX_AGING,            0x000170),
+       REG(SYS_RESET_CFG,                 0x000508),
+       REG(SYS_CMID,                      0x00050c),
+       REG(SYS_VLAN_ETYPE_CFG,            0x000510),
+       REG(SYS_PORT_MODE,                 0x000514),
+       REG(SYS_FRONT_PORT_MODE,           0x000548),
+       REG(SYS_FRM_AGING,                 0x000574),
+       REG(SYS_STAT_CFG,                  0x000578),
+       REG(SYS_SW_STATUS,                 0x00057c),
+       REG(SYS_MISC_CFG,                  0x0005ac),
+       REG(SYS_REW_MAC_HIGH_CFG,          0x0005b0),
+       REG(SYS_REW_MAC_LOW_CFG,           0x0005dc),
+       REG(SYS_CM_ADDR,                   0x000500),
+       REG(SYS_CM_DATA,                   0x000504),
+       REG(SYS_PAUSE_CFG,                 0x000608),
+       REG(SYS_PAUSE_TOT_CFG,             0x000638),
+       REG(SYS_ATOP,                      0x00063c),
+       REG(SYS_ATOP_TOT_CFG,              0x00066c),
+       REG(SYS_MAC_FC_CFG,                0x000670),
+       REG(SYS_MMGT,                      0x00069c),
+       REG(SYS_MMGT_FAST,                 0x0006a0),
+       REG(SYS_EVENTS_DIF,                0x0006a4),
+       REG(SYS_EVENTS_CORE,               0x0006b4),
+       REG(SYS_CNT,                       0x000000),
+       REG(SYS_PTP_STATUS,                0x0006b8),
+       REG(SYS_PTP_TXSTAMP,               0x0006bc),
+       REG(SYS_PTP_NXT,                   0x0006c0),
+       REG(SYS_PTP_CFG,                   0x0006c4),
+};
+
+static const u32 *ocelot_regmap[] = {
+       [ANA] = ocelot_ana_regmap,
+       [QS] = ocelot_qs_regmap,
+       [HSIO] = ocelot_hsio_regmap,
+       [QSYS] = ocelot_qsys_regmap,
+       [REW] = ocelot_rew_regmap,
+       [SYS] = ocelot_sys_regmap,
+};
+
+static const struct reg_field ocelot_regfields[] = {
+       [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 11, 11),
+       [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 10),
+       [ANA_ANEVENTS_MSTI_DROP] = REG_FIELD(ANA_ANEVENTS, 27, 27),
+       [ANA_ANEVENTS_ACLKILL] = REG_FIELD(ANA_ANEVENTS, 26, 26),
+       [ANA_ANEVENTS_ACLUSED] = REG_FIELD(ANA_ANEVENTS, 25, 25),
+       [ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 24, 24),
+       [ANA_ANEVENTS_VS2TTL1] = REG_FIELD(ANA_ANEVENTS, 23, 23),
+       [ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 22, 22),
+       [ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 21, 21),
+       [ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 20, 20),
+       [ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 19, 19),
+       [ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 18, 18),
+       [ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 17, 17),
+       [ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 16, 16),
+       [ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 15, 15),
+       [ANA_ANEVENTS_DROPPED] = REG_FIELD(ANA_ANEVENTS, 14, 14),
+       [ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 13, 13),
+       [ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 12, 12),
+       [ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 11, 11),
+       [ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 10, 10),
+       [ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 9, 9),
+       [ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 8, 8),
+       [ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 7, 7),
+       [ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6),
+       [ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5),
+       [ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 4, 4),
+       [ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 3, 3),
+       [ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 2, 2),
+       [ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 1, 1),
+       [ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 0, 0),
+       [ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 18, 18),
+       [ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 10, 11),
+       [ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 9),
+       [QSYS_TIMED_FRAME_ENTRY_TFRM_VLD] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 20, 20),
+       [QSYS_TIMED_FRAME_ENTRY_TFRM_FP] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 8, 19),
+       [QSYS_TIMED_FRAME_ENTRY_TFRM_PORTNO] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 4, 7),
+       [QSYS_TIMED_FRAME_ENTRY_TFRM_TM_SEL] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 1, 3),
+       [QSYS_TIMED_FRAME_ENTRY_TFRM_TM_T] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 0, 0),
+       [SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 2, 2),
+       [SYS_RESET_CFG_MEM_ENA] = REG_FIELD(SYS_RESET_CFG, 1, 1),
+       [SYS_RESET_CFG_MEM_INIT] = REG_FIELD(SYS_RESET_CFG, 0, 0),
+};
+
+static const struct ocelot_stat_layout ocelot_stats_layout[] = {
+       { .name = "rx_octets", .offset = 0x00, },
+       { .name = "rx_unicast", .offset = 0x01, },
+       { .name = "rx_multicast", .offset = 0x02, },
+       { .name = "rx_broadcast", .offset = 0x03, },
+       { .name = "rx_shorts", .offset = 0x04, },
+       { .name = "rx_fragments", .offset = 0x05, },
+       { .name = "rx_jabbers", .offset = 0x06, },
+       { .name = "rx_crc_align_errs", .offset = 0x07, },
+       { .name = "rx_sym_errs", .offset = 0x08, },
+       { .name = "rx_frames_below_65_octets", .offset = 0x09, },
+       { .name = "rx_frames_65_to_127_octets", .offset = 0x0A, },
+       { .name = "rx_frames_128_to_255_octets", .offset = 0x0B, },
+       { .name = "rx_frames_256_to_511_octets", .offset = 0x0C, },
+       { .name = "rx_frames_512_to_1023_octets", .offset = 0x0D, },
+       { .name = "rx_frames_1024_to_1526_octets", .offset = 0x0E, },
+       { .name = "rx_frames_over_1526_octets", .offset = 0x0F, },
+       { .name = "rx_pause", .offset = 0x10, },
+       { .name = "rx_control", .offset = 0x11, },
+       { .name = "rx_longs", .offset = 0x12, },
+       { .name = "rx_classified_drops", .offset = 0x13, },
+       { .name = "rx_red_prio_0", .offset = 0x14, },
+       { .name = "rx_red_prio_1", .offset = 0x15, },
+       { .name = "rx_red_prio_2", .offset = 0x16, },
+       { .name = "rx_red_prio_3", .offset = 0x17, },
+       { .name = "rx_red_prio_4", .offset = 0x18, },
+       { .name = "rx_red_prio_5", .offset = 0x19, },
+       { .name = "rx_red_prio_6", .offset = 0x1A, },
+       { .name = "rx_red_prio_7", .offset = 0x1B, },
+       { .name = "rx_yellow_prio_0", .offset = 0x1C, },
+       { .name = "rx_yellow_prio_1", .offset = 0x1D, },
+       { .name = "rx_yellow_prio_2", .offset = 0x1E, },
+       { .name = "rx_yellow_prio_3", .offset = 0x1F, },
+       { .name = "rx_yellow_prio_4", .offset = 0x20, },
+       { .name = "rx_yellow_prio_5", .offset = 0x21, },
+       { .name = "rx_yellow_prio_6", .offset = 0x22, },
+       { .name = "rx_yellow_prio_7", .offset = 0x23, },
+       { .name = "rx_green_prio_0", .offset = 0x24, },
+       { .name = "rx_green_prio_1", .offset = 0x25, },
+       { .name = "rx_green_prio_2", .offset = 0x26, },
+       { .name = "rx_green_prio_3", .offset = 0x27, },
+       { .name = "rx_green_prio_4", .offset = 0x28, },
+       { .name = "rx_green_prio_5", .offset = 0x29, },
+       { .name = "rx_green_prio_6", .offset = 0x2A, },
+       { .name = "rx_green_prio_7", .offset = 0x2B, },
+       { .name = "tx_octets", .offset = 0x40, },
+       { .name = "tx_unicast", .offset = 0x41, },
+       { .name = "tx_multicast", .offset = 0x42, },
+       { .name = "tx_broadcast", .offset = 0x43, },
+       { .name = "tx_collision", .offset = 0x44, },
+       { .name = "tx_drops", .offset = 0x45, },
+       { .name = "tx_pause", .offset = 0x46, },
+       { .name = "tx_frames_below_65_octets", .offset = 0x47, },
+       { .name = "tx_frames_65_to_127_octets", .offset = 0x48, },
+       { .name = "tx_frames_128_255_octets", .offset = 0x49, },
+       { .name = "tx_frames_256_511_octets", .offset = 0x4A, },
+       { .name = "tx_frames_512_1023_octets", .offset = 0x4B, },
+       { .name = "tx_frames_1024_1526_octets", .offset = 0x4C, },
+       { .name = "tx_frames_over_1526_octets", .offset = 0x4D, },
+       { .name = "tx_yellow_prio_0", .offset = 0x4E, },
+       { .name = "tx_yellow_prio_1", .offset = 0x4F, },
+       { .name = "tx_yellow_prio_2", .offset = 0x50, },
+       { .name = "tx_yellow_prio_3", .offset = 0x51, },
+       { .name = "tx_yellow_prio_4", .offset = 0x52, },
+       { .name = "tx_yellow_prio_5", .offset = 0x53, },
+       { .name = "tx_yellow_prio_6", .offset = 0x54, },
+       { .name = "tx_yellow_prio_7", .offset = 0x55, },
+       { .name = "tx_green_prio_0", .offset = 0x56, },
+       { .name = "tx_green_prio_1", .offset = 0x57, },
+       { .name = "tx_green_prio_2", .offset = 0x58, },
+       { .name = "tx_green_prio_3", .offset = 0x59, },
+       { .name = "tx_green_prio_4", .offset = 0x5A, },
+       { .name = "tx_green_prio_5", .offset = 0x5B, },
+       { .name = "tx_green_prio_6", .offset = 0x5C, },
+       { .name = "tx_green_prio_7", .offset = 0x5D, },
+       { .name = "tx_aged", .offset = 0x5E, },
+       { .name = "drop_local", .offset = 0x80, },
+       { .name = "drop_tail", .offset = 0x81, },
+       { .name = "drop_yellow_prio_0", .offset = 0x82, },
+       { .name = "drop_yellow_prio_1", .offset = 0x83, },
+       { .name = "drop_yellow_prio_2", .offset = 0x84, },
+       { .name = "drop_yellow_prio_3", .offset = 0x85, },
+       { .name = "drop_yellow_prio_4", .offset = 0x86, },
+       { .name = "drop_yellow_prio_5", .offset = 0x87, },
+       { .name = "drop_yellow_prio_6", .offset = 0x88, },
+       { .name = "drop_yellow_prio_7", .offset = 0x89, },
+       { .name = "drop_green_prio_0", .offset = 0x8A, },
+       { .name = "drop_green_prio_1", .offset = 0x8B, },
+       { .name = "drop_green_prio_2", .offset = 0x8C, },
+       { .name = "drop_green_prio_3", .offset = 0x8D, },
+       { .name = "drop_green_prio_4", .offset = 0x8E, },
+       { .name = "drop_green_prio_5", .offset = 0x8F, },
+       { .name = "drop_green_prio_6", .offset = 0x90, },
+       { .name = "drop_green_prio_7", .offset = 0x91, },
+};
+
+static void ocelot_pll5_init(struct ocelot *ocelot)
+{
+       /* Configure PLL5. This will need a proper CCF driver
+        * The values are coming from the VTSS API for Ocelot
+        */
+       ocelot_write(ocelot, HSIO_PLL5G_CFG4_IB_CTRL(0x7600) |
+                    HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8), HSIO_PLL5G_CFG4);
+       ocelot_write(ocelot, HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) |
+                    HSIO_PLL5G_CFG0_CPU_CLK_DIV(2) |
+                    HSIO_PLL5G_CFG0_ENA_BIAS |
+                    HSIO_PLL5G_CFG0_ENA_VCO_BUF |
+                    HSIO_PLL5G_CFG0_ENA_CP1 |
+                    HSIO_PLL5G_CFG0_SELCPI(2) |
+                    HSIO_PLL5G_CFG0_LOOP_BW_RES(0xe) |
+                    HSIO_PLL5G_CFG0_SELBGV820(4) |
+                    HSIO_PLL5G_CFG0_DIV4 |
+                    HSIO_PLL5G_CFG0_ENA_CLKTREE |
+                    HSIO_PLL5G_CFG0_ENA_LANE, HSIO_PLL5G_CFG0);
+       ocelot_write(ocelot, HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET |
+                    HSIO_PLL5G_CFG2_EN_RESET_OVERRUN |
+                    HSIO_PLL5G_CFG2_GAIN_TEST(0x8) |
+                    HSIO_PLL5G_CFG2_ENA_AMPCTRL |
+                    HSIO_PLL5G_CFG2_PWD_AMPCTRL_N |
+                    HSIO_PLL5G_CFG2_AMPC_SEL(0x10), HSIO_PLL5G_CFG2);
+}
+
+int ocelot_chip_init(struct ocelot *ocelot)
+{
+       int ret;
+
+       ocelot->map = ocelot_regmap;
+       ocelot->stats_layout = ocelot_stats_layout;
+       ocelot->num_stats = ARRAY_SIZE(ocelot_stats_layout);
+       ocelot->shared_queue_sz = 224 * 1024;
+
+       ret = ocelot_regfields_init(ocelot, ocelot_regfields);
+       if (ret)
+               return ret;
+
+       ocelot_pll5_init(ocelot);
+
+       eth_random_addr(ocelot->base_mac);
+       ocelot->base_mac[5] &= 0xf0;
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_chip_init);
diff --git a/drivers/net/ethernet/mscc/ocelot_rew.h b/drivers/net/ethernet/mscc/ocelot_rew.h
new file mode 100644 (file)
index 0000000..210914b
--- /dev/null
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_REW_H_
+#define _MSCC_OCELOT_REW_H_
+
+#define REW_PORT_VLAN_CFG_GSZ                             0x80
+
+#define REW_PORT_VLAN_CFG_PORT_TPID(x)                    (((x) << 16) & GENMASK(31, 16))
+#define REW_PORT_VLAN_CFG_PORT_TPID_M                     GENMASK(31, 16)
+#define REW_PORT_VLAN_CFG_PORT_TPID_X(x)                  (((x) & GENMASK(31, 16)) >> 16)
+#define REW_PORT_VLAN_CFG_PORT_DEI                        BIT(15)
+#define REW_PORT_VLAN_CFG_PORT_PCP(x)                     (((x) << 12) & GENMASK(14, 12))
+#define REW_PORT_VLAN_CFG_PORT_PCP_M                      GENMASK(14, 12)
+#define REW_PORT_VLAN_CFG_PORT_PCP_X(x)                   (((x) & GENMASK(14, 12)) >> 12)
+#define REW_PORT_VLAN_CFG_PORT_VID(x)                     ((x) & GENMASK(11, 0))
+#define REW_PORT_VLAN_CFG_PORT_VID_M                      GENMASK(11, 0)
+
+#define REW_TAG_CFG_GSZ                                   0x80
+
+#define REW_TAG_CFG_TAG_CFG(x)                            (((x) << 7) & GENMASK(8, 7))
+#define REW_TAG_CFG_TAG_CFG_M                             GENMASK(8, 7)
+#define REW_TAG_CFG_TAG_CFG_X(x)                          (((x) & GENMASK(8, 7)) >> 7)
+#define REW_TAG_CFG_TAG_TPID_CFG(x)                       (((x) << 5) & GENMASK(6, 5))
+#define REW_TAG_CFG_TAG_TPID_CFG_M                        GENMASK(6, 5)
+#define REW_TAG_CFG_TAG_TPID_CFG_X(x)                     (((x) & GENMASK(6, 5)) >> 5)
+#define REW_TAG_CFG_TAG_VID_CFG                           BIT(4)
+#define REW_TAG_CFG_TAG_PCP_CFG(x)                        (((x) << 2) & GENMASK(3, 2))
+#define REW_TAG_CFG_TAG_PCP_CFG_M                         GENMASK(3, 2)
+#define REW_TAG_CFG_TAG_PCP_CFG_X(x)                      (((x) & GENMASK(3, 2)) >> 2)
+#define REW_TAG_CFG_TAG_DEI_CFG(x)                        ((x) & GENMASK(1, 0))
+#define REW_TAG_CFG_TAG_DEI_CFG_M                         GENMASK(1, 0)
+
+#define REW_PORT_CFG_GSZ                                  0x80
+
+#define REW_PORT_CFG_ES0_EN                               BIT(5)
+#define REW_PORT_CFG_FCS_UPDATE_NONCPU_CFG(x)             (((x) << 3) & GENMASK(4, 3))
+#define REW_PORT_CFG_FCS_UPDATE_NONCPU_CFG_M              GENMASK(4, 3)
+#define REW_PORT_CFG_FCS_UPDATE_NONCPU_CFG_X(x)           (((x) & GENMASK(4, 3)) >> 3)
+#define REW_PORT_CFG_FCS_UPDATE_CPU_ENA                   BIT(2)
+#define REW_PORT_CFG_FLUSH_ENA                            BIT(1)
+#define REW_PORT_CFG_AGE_DIS                              BIT(0)
+
+#define REW_DSCP_CFG_GSZ                                  0x80
+
+#define REW_PCP_DEI_QOS_MAP_CFG_GSZ                       0x80
+#define REW_PCP_DEI_QOS_MAP_CFG_RSZ                       0x4
+
+#define REW_PCP_DEI_QOS_MAP_CFG_DEI_QOS_VAL               BIT(3)
+#define REW_PCP_DEI_QOS_MAP_CFG_PCP_QOS_VAL(x)            ((x) & GENMASK(2, 0))
+#define REW_PCP_DEI_QOS_MAP_CFG_PCP_QOS_VAL_M             GENMASK(2, 0)
+
+#define REW_PTP_CFG_GSZ                                   0x80
+
+#define REW_PTP_CFG_PTP_BACKPLANE_MODE                    BIT(7)
+#define REW_PTP_CFG_GP_CFG_UNUSED(x)                      (((x) << 3) & GENMASK(6, 3))
+#define REW_PTP_CFG_GP_CFG_UNUSED_M                       GENMASK(6, 3)
+#define REW_PTP_CFG_GP_CFG_UNUSED_X(x)                    (((x) & GENMASK(6, 3)) >> 3)
+#define REW_PTP_CFG_PTP_1STEP_DIS                         BIT(2)
+#define REW_PTP_CFG_PTP_2STEP_DIS                         BIT(1)
+#define REW_PTP_CFG_PTP_UDP_KEEP                          BIT(0)
+
+#define REW_PTP_DLY1_CFG_GSZ                              0x80
+
+#define REW_RED_TAG_CFG_GSZ                               0x80
+
+#define REW_RED_TAG_CFG_RED_TAG_CFG                       BIT(0)
+
+#define REW_DSCP_REMAP_DP1_CFG_RSZ                        0x4
+
+#define REW_DSCP_REMAP_CFG_RSZ                            0x4
+
+#define REW_REW_STICKY_ES0_TAGB_PUSH_FAILED               BIT(0)
+
+#define REW_PPT_RSZ                                       0x4
+
+#endif
diff --git a/drivers/net/ethernet/mscc/ocelot_sys.h b/drivers/net/ethernet/mscc/ocelot_sys.h
new file mode 100644 (file)
index 0000000..16f91e1
--- /dev/null
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#ifndef _MSCC_OCELOT_SYS_H_
+#define _MSCC_OCELOT_SYS_H_
+
+#define SYS_COUNT_RX_OCTETS_RSZ                           0x4
+
+#define SYS_COUNT_TX_OCTETS_RSZ                           0x4
+
+#define SYS_PORT_MODE_RSZ                                 0x4
+
+#define SYS_PORT_MODE_DATA_WO_TS(x)                       (((x) << 5) & GENMASK(6, 5))
+#define SYS_PORT_MODE_DATA_WO_TS_M                        GENMASK(6, 5)
+#define SYS_PORT_MODE_DATA_WO_TS_X(x)                     (((x) & GENMASK(6, 5)) >> 5)
+#define SYS_PORT_MODE_INCL_INJ_HDR(x)                     (((x) << 3) & GENMASK(4, 3))
+#define SYS_PORT_MODE_INCL_INJ_HDR_M                      GENMASK(4, 3)
+#define SYS_PORT_MODE_INCL_INJ_HDR_X(x)                   (((x) & GENMASK(4, 3)) >> 3)
+#define SYS_PORT_MODE_INCL_XTR_HDR(x)                     (((x) << 1) & GENMASK(2, 1))
+#define SYS_PORT_MODE_INCL_XTR_HDR_M                      GENMASK(2, 1)
+#define SYS_PORT_MODE_INCL_XTR_HDR_X(x)                   (((x) & GENMASK(2, 1)) >> 1)
+#define SYS_PORT_MODE_INJ_HDR_ERR                         BIT(0)
+
+#define SYS_FRONT_PORT_MODE_RSZ                           0x4
+
+#define SYS_FRONT_PORT_MODE_HDX_MODE                      BIT(0)
+
+#define SYS_FRM_AGING_AGE_TX_ENA                          BIT(20)
+#define SYS_FRM_AGING_MAX_AGE(x)                          ((x) & GENMASK(19, 0))
+#define SYS_FRM_AGING_MAX_AGE_M                           GENMASK(19, 0)
+
+#define SYS_STAT_CFG_STAT_CLEAR_SHOT(x)                   (((x) << 10) & GENMASK(16, 10))
+#define SYS_STAT_CFG_STAT_CLEAR_SHOT_M                    GENMASK(16, 10)
+#define SYS_STAT_CFG_STAT_CLEAR_SHOT_X(x)                 (((x) & GENMASK(16, 10)) >> 10)
+#define SYS_STAT_CFG_STAT_VIEW(x)                         ((x) & GENMASK(9, 0))
+#define SYS_STAT_CFG_STAT_VIEW_M                          GENMASK(9, 0)
+
+#define SYS_SW_STATUS_RSZ                                 0x4
+
+#define SYS_SW_STATUS_PORT_RX_PAUSED                      BIT(0)
+
+#define SYS_MISC_CFG_PTP_RSRV_CLR                         BIT(1)
+#define SYS_MISC_CFG_PTP_DIS_NEG_RO                       BIT(0)
+
+#define SYS_REW_MAC_HIGH_CFG_RSZ                          0x4
+
+#define SYS_REW_MAC_LOW_CFG_RSZ                           0x4
+
+#define SYS_TIMESTAMP_OFFSET_ETH_TYPE_CFG(x)              (((x) << 6) & GENMASK(21, 6))
+#define SYS_TIMESTAMP_OFFSET_ETH_TYPE_CFG_M               GENMASK(21, 6)
+#define SYS_TIMESTAMP_OFFSET_ETH_TYPE_CFG_X(x)            (((x) & GENMASK(21, 6)) >> 6)
+#define SYS_TIMESTAMP_OFFSET_TIMESTAMP_OFFSET(x)          ((x) & GENMASK(5, 0))
+#define SYS_TIMESTAMP_OFFSET_TIMESTAMP_OFFSET_M           GENMASK(5, 0)
+
+#define SYS_PAUSE_CFG_RSZ                                 0x4
+
+#define SYS_PAUSE_CFG_PAUSE_START(x)                      (((x) << 10) & GENMASK(18, 10))
+#define SYS_PAUSE_CFG_PAUSE_START_M                       GENMASK(18, 10)
+#define SYS_PAUSE_CFG_PAUSE_START_X(x)                    (((x) & GENMASK(18, 10)) >> 10)
+#define SYS_PAUSE_CFG_PAUSE_STOP(x)                       (((x) << 1) & GENMASK(9, 1))
+#define SYS_PAUSE_CFG_PAUSE_STOP_M                        GENMASK(9, 1)
+#define SYS_PAUSE_CFG_PAUSE_STOP_X(x)                     (((x) & GENMASK(9, 1)) >> 1)
+#define SYS_PAUSE_CFG_PAUSE_ENA                           BIT(0)
+
+#define SYS_PAUSE_TOT_CFG_PAUSE_TOT_START(x)              (((x) << 9) & GENMASK(17, 9))
+#define SYS_PAUSE_TOT_CFG_PAUSE_TOT_START_M               GENMASK(17, 9)
+#define SYS_PAUSE_TOT_CFG_PAUSE_TOT_START_X(x)            (((x) & GENMASK(17, 9)) >> 9)
+#define SYS_PAUSE_TOT_CFG_PAUSE_TOT_STOP(x)               ((x) & GENMASK(8, 0))
+#define SYS_PAUSE_TOT_CFG_PAUSE_TOT_STOP_M                GENMASK(8, 0)
+
+#define SYS_ATOP_RSZ                                      0x4
+
+#define SYS_MAC_FC_CFG_RSZ                                0x4
+
+#define SYS_MAC_FC_CFG_FC_LINK_SPEED(x)                   (((x) << 26) & GENMASK(27, 26))
+#define SYS_MAC_FC_CFG_FC_LINK_SPEED_M                    GENMASK(27, 26)
+#define SYS_MAC_FC_CFG_FC_LINK_SPEED_X(x)                 (((x) & GENMASK(27, 26)) >> 26)
+#define SYS_MAC_FC_CFG_FC_LATENCY_CFG(x)                  (((x) << 20) & GENMASK(25, 20))
+#define SYS_MAC_FC_CFG_FC_LATENCY_CFG_M                   GENMASK(25, 20)
+#define SYS_MAC_FC_CFG_FC_LATENCY_CFG_X(x)                (((x) & GENMASK(25, 20)) >> 20)
+#define SYS_MAC_FC_CFG_ZERO_PAUSE_ENA                     BIT(18)
+#define SYS_MAC_FC_CFG_TX_FC_ENA                          BIT(17)
+#define SYS_MAC_FC_CFG_RX_FC_ENA                          BIT(16)
+#define SYS_MAC_FC_CFG_PAUSE_VAL_CFG(x)                   ((x) & GENMASK(15, 0))
+#define SYS_MAC_FC_CFG_PAUSE_VAL_CFG_M                    GENMASK(15, 0)
+
+#define SYS_MMGT_RELCNT(x)                                (((x) << 16) & GENMASK(31, 16))
+#define SYS_MMGT_RELCNT_M                                 GENMASK(31, 16)
+#define SYS_MMGT_RELCNT_X(x)                              (((x) & GENMASK(31, 16)) >> 16)
+#define SYS_MMGT_FREECNT(x)                               ((x) & GENMASK(15, 0))
+#define SYS_MMGT_FREECNT_M                                GENMASK(15, 0)
+
+#define SYS_MMGT_FAST_FREEVLD(x)                          (((x) << 4) & GENMASK(7, 4))
+#define SYS_MMGT_FAST_FREEVLD_M                           GENMASK(7, 4)
+#define SYS_MMGT_FAST_FREEVLD_X(x)                        (((x) & GENMASK(7, 4)) >> 4)
+#define SYS_MMGT_FAST_RELVLD(x)                           ((x) & GENMASK(3, 0))
+#define SYS_MMGT_FAST_RELVLD_M                            GENMASK(3, 0)
+
+#define SYS_EVENTS_DIF_RSZ                                0x4
+
+#define SYS_EVENTS_DIF_EV_DRX(x)                          (((x) << 6) & GENMASK(8, 6))
+#define SYS_EVENTS_DIF_EV_DRX_M                           GENMASK(8, 6)
+#define SYS_EVENTS_DIF_EV_DRX_X(x)                        (((x) & GENMASK(8, 6)) >> 6)
+#define SYS_EVENTS_DIF_EV_DTX(x)                          ((x) & GENMASK(5, 0))
+#define SYS_EVENTS_DIF_EV_DTX_M                           GENMASK(5, 0)
+
+#define SYS_EVENTS_CORE_EV_FWR                            BIT(2)
+#define SYS_EVENTS_CORE_EV_ANA(x)                         ((x) & GENMASK(1, 0))
+#define SYS_EVENTS_CORE_EV_ANA_M                          GENMASK(1, 0)
+
+#define SYS_CNT_GSZ                                       0x4
+
+#define SYS_PTP_STATUS_PTP_TXSTAMP_OAM                    BIT(29)
+#define SYS_PTP_STATUS_PTP_OVFL                           BIT(28)
+#define SYS_PTP_STATUS_PTP_MESS_VLD                       BIT(27)
+#define SYS_PTP_STATUS_PTP_MESS_ID(x)                     (((x) << 21) & GENMASK(26, 21))
+#define SYS_PTP_STATUS_PTP_MESS_ID_M                      GENMASK(26, 21)
+#define SYS_PTP_STATUS_PTP_MESS_ID_X(x)                   (((x) & GENMASK(26, 21)) >> 21)
+#define SYS_PTP_STATUS_PTP_MESS_TXPORT(x)                 (((x) << 16) & GENMASK(20, 16))
+#define SYS_PTP_STATUS_PTP_MESS_TXPORT_M                  GENMASK(20, 16)
+#define SYS_PTP_STATUS_PTP_MESS_TXPORT_X(x)               (((x) & GENMASK(20, 16)) >> 16)
+#define SYS_PTP_STATUS_PTP_MESS_SEQ_ID(x)                 ((x) & GENMASK(15, 0))
+#define SYS_PTP_STATUS_PTP_MESS_SEQ_ID_M                  GENMASK(15, 0)
+
+#define SYS_PTP_TXSTAMP_PTP_TXSTAMP(x)                    ((x) & GENMASK(29, 0))
+#define SYS_PTP_TXSTAMP_PTP_TXSTAMP_M                     GENMASK(29, 0)
+#define SYS_PTP_TXSTAMP_PTP_TXSTAMP_SEC                   BIT(31)
+
+#define SYS_PTP_NXT_PTP_NXT                               BIT(0)
+
+#define SYS_PTP_CFG_PTP_STAMP_WID(x)                      (((x) << 2) & GENMASK(7, 2))
+#define SYS_PTP_CFG_PTP_STAMP_WID_M                       GENMASK(7, 2)
+#define SYS_PTP_CFG_PTP_STAMP_WID_X(x)                    (((x) & GENMASK(7, 2)) >> 2)
+#define SYS_PTP_CFG_PTP_CF_ROLL_MODE(x)                   ((x) & GENMASK(1, 0))
+#define SYS_PTP_CFG_PTP_CF_ROLL_MODE_M                    GENMASK(1, 0)
+
+#define SYS_RAM_INIT_RAM_INIT                             BIT(1)
+#define SYS_RAM_INIT_RAM_CFG_HOOK                         BIT(0)
+
+#endif
index 6223930..c60da9e 100644 (file)
@@ -693,7 +693,7 @@ __vxge_hw_device_is_privilaged(u32 host_type, u32 func_id)
                VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM)
                return VXGE_HW_OK;
        else
-               return VXGE_HW_ERR_PRIVILAGED_OPEARATION;
+               return VXGE_HW_ERR_PRIVILEGED_OPERATION;
 }
 
 /*
@@ -1920,7 +1920,7 @@ enum vxge_hw_status vxge_hw_device_getpause_data(struct __vxge_hw_device *hldev,
        }
 
        if (!(hldev->access_rights & VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM)) {
-               status = VXGE_HW_ERR_PRIVILAGED_OPEARATION;
+               status = VXGE_HW_ERR_PRIVILEGED_OPERATION;
                goto exit;
        }
 
@@ -3153,7 +3153,7 @@ vxge_hw_mgmt_reg_read(struct __vxge_hw_device *hldev,
        case vxge_hw_mgmt_reg_type_mrpcim:
                if (!(hldev->access_rights &
                        VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM)) {
-                       status = VXGE_HW_ERR_PRIVILAGED_OPEARATION;
+                       status = VXGE_HW_ERR_PRIVILEGED_OPERATION;
                        break;
                }
                if (offset > sizeof(struct vxge_hw_mrpcim_reg) - 8) {
@@ -3165,7 +3165,7 @@ vxge_hw_mgmt_reg_read(struct __vxge_hw_device *hldev,
        case vxge_hw_mgmt_reg_type_srpcim:
                if (!(hldev->access_rights &
                        VXGE_HW_DEVICE_ACCESS_RIGHT_SRPCIM)) {
-                       status = VXGE_HW_ERR_PRIVILAGED_OPEARATION;
+                       status = VXGE_HW_ERR_PRIVILEGED_OPERATION;
                        break;
                }
                if (index > VXGE_HW_TITAN_SRPCIM_REG_SPACES - 1) {
@@ -3279,7 +3279,7 @@ vxge_hw_mgmt_reg_write(struct __vxge_hw_device *hldev,
        case vxge_hw_mgmt_reg_type_mrpcim:
                if (!(hldev->access_rights &
                        VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM)) {
-                       status = VXGE_HW_ERR_PRIVILAGED_OPEARATION;
+                       status = VXGE_HW_ERR_PRIVILEGED_OPERATION;
                        break;
                }
                if (offset > sizeof(struct vxge_hw_mrpcim_reg) - 8) {
@@ -3291,7 +3291,7 @@ vxge_hw_mgmt_reg_write(struct __vxge_hw_device *hldev,
        case vxge_hw_mgmt_reg_type_srpcim:
                if (!(hldev->access_rights &
                        VXGE_HW_DEVICE_ACCESS_RIGHT_SRPCIM)) {
-                       status = VXGE_HW_ERR_PRIVILAGED_OPEARATION;
+                       status = VXGE_HW_ERR_PRIVILEGED_OPERATION;
                        break;
                }
                if (index > VXGE_HW_TITAN_SRPCIM_REG_SPACES - 1) {
index cfa9704..d743a37 100644 (file)
@@ -127,7 +127,7 @@ enum vxge_hw_status {
        VXGE_HW_ERR_INVALID_TCODE                 = VXGE_HW_BASE_ERR + 14,
        VXGE_HW_ERR_INVALID_BLOCK_SIZE            = VXGE_HW_BASE_ERR + 15,
        VXGE_HW_ERR_INVALID_STATE                 = VXGE_HW_BASE_ERR + 16,
-       VXGE_HW_ERR_PRIVILAGED_OPEARATION         = VXGE_HW_BASE_ERR + 17,
+       VXGE_HW_ERR_PRIVILEGED_OPERATION          = VXGE_HW_BASE_ERR + 17,
        VXGE_HW_ERR_INVALID_PORT                  = VXGE_HW_BASE_ERR + 18,
        VXGE_HW_ERR_FIFO                          = VXGE_HW_BASE_ERR + 19,
        VXGE_HW_ERR_VPATH                         = VXGE_HW_BASE_ERR + 20,
index 0452848..03c3d12 100644 (file)
@@ -276,7 +276,7 @@ static void vxge_get_ethtool_stats(struct net_device *dev,
        *ptr++ = 0;
        status = vxge_hw_device_xmac_stats_get(hldev, xmac_stats);
        if (status != VXGE_HW_OK) {
-               if (status != VXGE_HW_ERR_PRIVILAGED_OPEARATION) {
+               if (status != VXGE_HW_ERR_PRIVILEGED_OPERATION) {
                        vxge_debug_init(VXGE_ERR,
                                "%s : %d Failure in getting xmac stats",
                                __func__, __LINE__);
index b2299f2..a8918bb 100644 (file)
@@ -3484,11 +3484,11 @@ static int vxge_device_register(struct __vxge_hw_device *hldev,
                                0,
                                &stat);
 
-       if (status == VXGE_HW_ERR_PRIVILAGED_OPEARATION)
+       if (status == VXGE_HW_ERR_PRIVILEGED_OPERATION)
                vxge_debug_init(
                        vxge_hw_device_trace_level_get(hldev),
                        "%s: device stats clear returns"
-                       "VXGE_HW_ERR_PRIVILAGED_OPEARATION", ndev->name);
+                       "VXGE_HW_ERR_PRIVILEGED_OPERATION", ndev->name);
 
        vxge_debug_entryexit(vxge_hw_device_trace_level_get(hldev),
                "%s: %s:%d  Exiting...",
index ae0c46b..66f15b0 100644 (file)
@@ -36,6 +36,19 @@ config NFP_APP_FLOWER
          either directly, with Open vSwitch, or any other way.  Note that
          TC Flower offload requires specific FW to work.
 
+config NFP_APP_ABM_NIC
+       bool "NFP4000/NFP6000 Advanced buffer management NIC support"
+       depends on NFP
+       depends on NET_SWITCHDEV
+       default y
+       help
+         Enable driver support for Advanced buffer management NIC on NFP.
+         ABM NIC allows advanced configuration of queuing and scheduling
+         of packets, including ECN marking. Say Y, if you are planning to
+         use one of the NFP4000 and NFP6000 platforms which support this
+         functionality.
+         Code will be built into the nfp.ko driver.
+
 config NFP_DEBUG
        bool "Debug support for Netronome(R) NFP4000/NFP6000 NIC drivers"
        depends on NFP
index d5866d7..6373f56 100644 (file)
@@ -30,6 +30,7 @@ nfp-objs := \
            nfp_net_sriov.o \
            nfp_netvf_main.o \
            nfp_port.o \
+           nfp_shared_buf.o \
            nic/main.o
 
 ifeq ($(CONFIG_NFP_APP_FLOWER),y)
@@ -52,4 +53,10 @@ nfp-objs += \
            bpf/jit.o
 endif
 
+ifeq ($(CONFIG_NFP_APP_ABM_NIC),y)
+nfp-objs += \
+           abm/ctrl.o \
+           abm/main.o
+endif
+
 nfp-$(CONFIG_NFP_DEBUG) += nfp_net_debugfs.o
diff --git a/drivers/net/ethernet/netronome/nfp/abm/ctrl.c b/drivers/net/ethernet/netronome/nfp/abm/ctrl.c
new file mode 100644 (file)
index 0000000..e40f6f0
--- /dev/null
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: (GPL-2.0 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.
+ *
+ * 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 "../nfpcore/nfp_cpp.h"
+#include "../nfp_app.h"
+#include "../nfp_main.h"
+#include "../nfp_net.h"
+#include "main.h"
+
+void 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;
+}
+
+int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm)
+{
+       struct nfp_pf *pf = abm->app->pf;
+       unsigned int pf_id;
+
+       pf_id = nfp_cppcore_pcie_unit(pf->cpp);
+       abm->pf_id = pf_id;
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/netronome/nfp/abm/main.c b/drivers/net/ethernet/netronome/nfp/abm/main.c
new file mode 100644 (file)
index 0000000..5a12bb2
--- /dev/null
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: (GPL-2.0 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.
+ *
+ * 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/bitfield.h>
+#include <linux/etherdevice.h>
+#include <linux/lockdep.h>
+#include <linux/netdevice.h>
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+
+#include "../nfpcore/nfp.h"
+#include "../nfpcore/nfp_cpp.h"
+#include "../nfpcore/nfp_nsp.h"
+#include "../nfp_app.h"
+#include "../nfp_main.h"
+#include "../nfp_net.h"
+#include "../nfp_net_repr.h"
+#include "../nfp_port.h"
+#include "main.h"
+
+static u32 nfp_abm_portid(enum nfp_repr_type rtype, unsigned int id)
+{
+       return FIELD_PREP(NFP_ABM_PORTID_TYPE, rtype) |
+              FIELD_PREP(NFP_ABM_PORTID_ID, id);
+}
+
+static struct net_device *nfp_abm_repr_get(struct nfp_app *app, u32 port_id)
+{
+       enum nfp_repr_type rtype;
+       struct nfp_reprs *reprs;
+       u8 port;
+
+       rtype = FIELD_GET(NFP_ABM_PORTID_TYPE, port_id);
+       port = FIELD_GET(NFP_ABM_PORTID_ID, port_id);
+
+       reprs = rcu_dereference(app->reprs[rtype]);
+       if (!reprs)
+               return NULL;
+
+       if (port >= reprs->num_reprs)
+               return NULL;
+
+       return rcu_dereference(reprs->reprs[port]);
+}
+
+static int
+nfp_abm_spawn_repr(struct nfp_app *app, struct nfp_abm_link *alink,
+                  enum nfp_port_type ptype)
+{
+       struct net_device *netdev;
+       enum nfp_repr_type rtype;
+       struct nfp_reprs *reprs;
+       struct nfp_repr *repr;
+       struct nfp_port *port;
+       int err;
+
+       if (ptype == NFP_PORT_PHYS_PORT)
+               rtype = NFP_REPR_TYPE_PHYS_PORT;
+       else
+               rtype = NFP_REPR_TYPE_PF;
+
+       netdev = nfp_repr_alloc(app);
+       if (!netdev)
+               return -ENOMEM;
+       repr = netdev_priv(netdev);
+       repr->app_priv = alink;
+
+       port = nfp_port_alloc(app, ptype, netdev);
+       if (IS_ERR(port)) {
+               err = PTR_ERR(port);
+               goto err_free_repr;
+       }
+
+       if (ptype == NFP_PORT_PHYS_PORT) {
+               port->eth_forced = true;
+               err = nfp_port_init_phy_port(app->pf, app, port, alink->id);
+               if (err)
+                       goto err_free_port;
+       } else {
+               port->pf_id = alink->abm->pf_id;
+               port->pf_split = app->pf->max_data_vnics > 1;
+               port->pf_split_id = alink->id;
+               port->vnic = alink->vnic->dp.ctrl_bar;
+       }
+
+       SET_NETDEV_DEV(netdev, &alink->vnic->pdev->dev);
+       eth_hw_addr_random(netdev);
+
+       err = nfp_repr_init(app, netdev, nfp_abm_portid(rtype, alink->id),
+                           port, alink->vnic->dp.netdev);
+       if (err)
+               goto err_free_port;
+
+       reprs = nfp_reprs_get_locked(app, rtype);
+       WARN(nfp_repr_get_locked(app, reprs, alink->id), "duplicate repr");
+       rcu_assign_pointer(reprs->reprs[alink->id], netdev);
+
+       nfp_info(app->cpp, "%s Port %d Representor(%s) created\n",
+                ptype == NFP_PORT_PF_PORT ? "PCIe" : "Phys",
+                alink->id, netdev->name);
+
+       return 0;
+
+err_free_port:
+       nfp_port_free(port);
+err_free_repr:
+       nfp_repr_free(netdev);
+       return err;
+}
+
+static void
+nfp_abm_kill_repr(struct nfp_app *app, struct nfp_abm_link *alink,
+                 enum nfp_repr_type rtype)
+{
+       struct net_device *netdev;
+       struct nfp_reprs *reprs;
+
+       reprs = nfp_reprs_get_locked(app, rtype);
+       netdev = nfp_repr_get_locked(app, reprs, alink->id);
+       if (!netdev)
+               return;
+       rcu_assign_pointer(reprs->reprs[alink->id], NULL);
+       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));
+}
+
+static void
+nfp_abm_kill_reprs(struct nfp_abm *abm, struct nfp_abm_link *alink)
+{
+       nfp_abm_kill_repr(abm->app, alink, NFP_REPR_TYPE_PF);
+       nfp_abm_kill_repr(abm->app, alink, NFP_REPR_TYPE_PHYS_PORT);
+}
+
+static void nfp_abm_kill_reprs_all(struct nfp_abm *abm)
+{
+       struct nfp_pf *pf = abm->app->pf;
+       struct nfp_net *nn;
+
+       list_for_each_entry(nn, &pf->vnics, vnic_list)
+               nfp_abm_kill_reprs(abm, (struct nfp_abm_link *)nn->app_priv);
+}
+
+static enum devlink_eswitch_mode nfp_abm_eswitch_mode_get(struct nfp_app *app)
+{
+       struct nfp_abm *abm = app->priv;
+
+       return abm->eswitch_mode;
+}
+
+static int nfp_abm_eswitch_set_legacy(struct nfp_abm *abm)
+{
+       nfp_abm_kill_reprs_all(abm);
+
+       abm->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY;
+       return 0;
+}
+
+static void nfp_abm_eswitch_clean_up(struct nfp_abm *abm)
+{
+       if (abm->eswitch_mode != DEVLINK_ESWITCH_MODE_LEGACY)
+               WARN_ON(nfp_abm_eswitch_set_legacy(abm));
+}
+
+static int nfp_abm_eswitch_set_switchdev(struct nfp_abm *abm)
+{
+       struct nfp_app *app = abm->app;
+       struct nfp_pf *pf = app->pf;
+       struct nfp_net *nn;
+       int err;
+
+       list_for_each_entry(nn, &pf->vnics, vnic_list) {
+               struct nfp_abm_link *alink = nn->app_priv;
+
+               err = nfp_abm_spawn_repr(app, alink, NFP_PORT_PHYS_PORT);
+               if (err)
+                       goto err_kill_all_reprs;
+
+               err = nfp_abm_spawn_repr(app, alink, NFP_PORT_PF_PORT);
+               if (err)
+                       goto err_kill_all_reprs;
+       }
+
+       abm->eswitch_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
+       return 0;
+
+err_kill_all_reprs:
+       nfp_abm_kill_reprs_all(abm);
+       return err;
+}
+
+static int nfp_abm_eswitch_mode_set(struct nfp_app *app, u16 mode)
+{
+       struct nfp_abm *abm = app->priv;
+
+       if (abm->eswitch_mode == mode)
+               return 0;
+
+       switch (mode) {
+       case DEVLINK_ESWITCH_MODE_LEGACY:
+               return nfp_abm_eswitch_set_legacy(abm);
+       case DEVLINK_ESWITCH_MODE_SWITCHDEV:
+               return nfp_abm_eswitch_set_switchdev(abm);
+       default:
+               return -EINVAL;
+       }
+}
+
+static void
+nfp_abm_vnic_set_mac(struct nfp_pf *pf, struct nfp_abm *abm, struct nfp_net *nn,
+                    unsigned int id)
+{
+       struct nfp_eth_table_port *eth_port = &pf->eth_tbl->ports[id];
+       u8 mac_addr[ETH_ALEN];
+       const char *mac_str;
+       char name[32];
+
+       if (id > pf->eth_tbl->count) {
+               nfp_warn(pf->cpp, "No entry for persistent MAC address\n");
+               eth_hw_addr_random(nn->dp.netdev);
+               return;
+       }
+
+       snprintf(name, sizeof(name), "eth%u.mac.pf%u",
+                eth_port->eth_index, abm->pf_id);
+
+       mac_str = nfp_hwinfo_lookup(pf->hwinfo, name);
+       if (!mac_str) {
+               nfp_warn(pf->cpp, "Can't lookup persistent MAC address (%s)\n",
+                        name);
+               eth_hw_addr_random(nn->dp.netdev);
+               return;
+       }
+
+       if (sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+                  &mac_addr[0], &mac_addr[1], &mac_addr[2],
+                  &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) {
+               nfp_warn(pf->cpp, "Can't parse persistent MAC address (%s)\n",
+                        mac_str);
+               eth_hw_addr_random(nn->dp.netdev);
+               return;
+       }
+
+       ether_addr_copy(nn->dp.netdev->dev_addr, mac_addr);
+       ether_addr_copy(nn->dp.netdev->perm_addr, mac_addr);
+}
+
+static int
+nfp_abm_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id)
+{
+       struct nfp_eth_table_port *eth_port = &app->pf->eth_tbl->ports[id];
+       struct nfp_abm *abm = app->priv;
+       struct nfp_abm_link *alink;
+       int err;
+
+       alink = kzalloc(sizeof(*alink), GFP_KERNEL);
+       if (!alink)
+               return -ENOMEM;
+       nn->app_priv = alink;
+       alink->abm = abm;
+       alink->vnic = nn;
+       alink->id = id;
+
+       /* 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_alink;
+
+       netif_keep_dst(nn->dp.netdev);
+
+       nfp_abm_vnic_set_mac(app->pf, abm, nn, id);
+       nfp_abm_ctrl_read_params(alink);
+
+       return 0;
+
+err_free_alink:
+       kfree(alink);
+       return err;
+}
+
+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);
+       kfree(alink);
+}
+
+static int nfp_abm_init(struct nfp_app *app)
+{
+       struct nfp_pf *pf = app->pf;
+       struct nfp_reprs *reprs;
+       struct nfp_abm *abm;
+       int err;
+
+       if (!pf->eth_tbl) {
+               nfp_err(pf->cpp, "ABM NIC requires ETH table\n");
+               return -EINVAL;
+       }
+       if (pf->max_data_vnics != pf->eth_tbl->count) {
+               nfp_err(pf->cpp, "ETH entries don't match vNICs (%d vs %d)\n",
+                       pf->max_data_vnics, pf->eth_tbl->count);
+               return -EINVAL;
+       }
+       if (!pf->mac_stats_bar) {
+               nfp_warn(app->cpp, "ABM NIC requires mac_stats symbol\n");
+               return -EINVAL;
+       }
+
+       abm = kzalloc(sizeof(*abm), GFP_KERNEL);
+       if (!abm)
+               return -ENOMEM;
+       app->priv = abm;
+       abm->app = app;
+
+       err = nfp_abm_ctrl_find_addrs(abm);
+       if (err)
+               goto err_free_abm;
+
+       err = -ENOMEM;
+       reprs = nfp_reprs_alloc(pf->max_data_vnics);
+       if (!reprs)
+               goto err_free_abm;
+       RCU_INIT_POINTER(app->reprs[NFP_REPR_TYPE_PHYS_PORT], reprs);
+
+       reprs = nfp_reprs_alloc(pf->max_data_vnics);
+       if (!reprs)
+               goto err_free_phys;
+       RCU_INIT_POINTER(app->reprs[NFP_REPR_TYPE_PF], reprs);
+
+       return 0;
+
+err_free_phys:
+       nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT);
+err_free_abm:
+       kfree(abm);
+       app->priv = NULL;
+       return err;
+}
+
+static void nfp_abm_clean(struct nfp_app *app)
+{
+       struct nfp_abm *abm = app->priv;
+
+       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);
+       kfree(abm);
+       app->priv = NULL;
+}
+
+const struct nfp_app_type app_abm = {
+       .id             = NFP_APP_ACTIVE_BUFFER_MGMT_NIC,
+       .name           = "abm",
+
+       .init           = nfp_abm_init,
+       .clean          = nfp_abm_clean,
+
+       .vnic_alloc     = nfp_abm_vnic_alloc,
+       .vnic_free      = nfp_abm_vnic_free,
+
+       .eswitch_mode_get       = nfp_abm_eswitch_mode_get,
+       .eswitch_mode_set       = nfp_abm_eswitch_mode_set,
+
+       .repr_get       = nfp_abm_repr_get,
+};
diff --git a/drivers/net/ethernet/netronome/nfp/abm/main.h b/drivers/net/ethernet/netronome/nfp/abm/main.h
new file mode 100644 (file)
index 0000000..5938b69
--- /dev/null
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: (GPL-2.0 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.
+ *
+ * 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 __NFP_ABM_H__
+#define __NFP_ABM_H__ 1
+
+#include <net/devlink.h>
+
+struct nfp_app;
+struct nfp_net;
+
+#define NFP_ABM_PORTID_TYPE    GENMASK(23, 16)
+#define NFP_ABM_PORTID_ID      GENMASK(7, 0)
+
+/**
+ * struct nfp_abm - ABM NIC app structure
+ * @app:       back pointer to nfp_app
+ * @pf_id:     ID of our PF link
+ * @eswitch_mode:      devlink eswitch mode, advanced functions only visible
+ *                     in switchdev mode
+ */
+struct nfp_abm {
+       struct nfp_app *app;
+       unsigned int pf_id;
+       enum devlink_eswitch_mode eswitch_mode;
+};
+
+/**
+ * struct nfp_abm_link - port tuple of a ABM NIC
+ * @abm:       back pointer to nfp_abm
+ * @vnic:      data vNIC
+ * @id:                id of the data vNIC
+ * @queue_base:        id of base to host queue within PCIe (not QC idx)
+ */
+struct nfp_abm_link {
+       struct nfp_abm *abm;
+       struct nfp_net *vnic;
+       unsigned int id;
+       unsigned int queue_base;
+};
+
+void nfp_abm_ctrl_read_params(struct nfp_abm_link *alink);
+int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm);
+#endif
index 3dbc216..4c7972e 100644 (file)
@@ -50,6 +50,7 @@ enum bpf_cap_tlv_type {
        NFP_BPF_CAP_TYPE_ADJUST_HEAD    = 2,
        NFP_BPF_CAP_TYPE_MAPS           = 3,
        NFP_BPF_CAP_TYPE_RANDOM         = 4,
+       NFP_BPF_CAP_TYPE_QUEUE_SELECT   = 5,
 };
 
 struct nfp_bpf_cap_tlv_func {
index 326a208..a4d3da2 100644 (file)
@@ -42,6 +42,7 @@
 
 #include "main.h"
 #include "../nfp_asm.h"
+#include "../nfp_net_ctrl.h"
 
 /* --- NFP prog --- */
 /* Foreach "multiple" entries macros provide pos and next<n> pointers.
@@ -1470,6 +1471,38 @@ nfp_perf_event_output(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
        return 0;
 }
 
+static int
+nfp_queue_select(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+       u32 jmp_tgt;
+
+       jmp_tgt = nfp_prog_current_offset(nfp_prog) + 5;
+
+       /* Make sure the queue id fits into FW field */
+       emit_alu(nfp_prog, reg_none(), reg_a(meta->insn.src_reg * 2),
+                ALU_OP_AND_NOT_B, reg_imm(0xff));
+       emit_br(nfp_prog, BR_BEQ, jmp_tgt, 2);
+
+       /* Set the 'queue selected' bit and the queue value */
+       emit_shf(nfp_prog, pv_qsel_set(nfp_prog),
+                pv_qsel_set(nfp_prog), SHF_OP_OR, reg_imm(1),
+                SHF_SC_L_SHF, PKT_VEL_QSEL_SET_BIT);
+       emit_ld_field(nfp_prog,
+                     pv_qsel_val(nfp_prog), 0x1, reg_b(meta->insn.src_reg * 2),
+                     SHF_SC_NONE, 0);
+       /* Delay slots end here, we will jump over next instruction if queue
+        * value fits into the field.
+        */
+       emit_ld_field(nfp_prog,
+                     pv_qsel_val(nfp_prog), 0x1, reg_imm(NFP_NET_RXR_MAX),
+                     SHF_SC_NONE, 0);
+
+       if (!nfp_prog_confirm_current_offset(nfp_prog, jmp_tgt))
+               return -EINVAL;
+
+       return 0;
+}
+
 /* --- Callbacks --- */
 static int mov_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
@@ -2160,6 +2193,17 @@ mem_stx_stack(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
                            false, wrp_lmem_store);
 }
 
+static int mem_stx_xdp(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+       switch (meta->insn.off) {
+       case offsetof(struct xdp_md, rx_queue_index):
+               return nfp_queue_select(nfp_prog, meta);
+       }
+
+       WARN_ON_ONCE(1); /* verifier should have rejected bad accesses */
+       return -EOPNOTSUPP;
+}
+
 static int
 mem_stx(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
        unsigned int size)
@@ -2186,6 +2230,9 @@ static int mem_stx2(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 
 static int mem_stx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
+       if (meta->ptr.type == PTR_TO_CTX)
+               if (nfp_prog->type == BPF_PROG_TYPE_XDP)
+                       return mem_stx_xdp(nfp_prog, meta);
        return mem_stx(nfp_prog, meta, 4);
 }
 
index d72f9e7..fcdfb8e 100644 (file)
@@ -334,6 +334,13 @@ nfp_bpf_parse_cap_random(struct nfp_app_bpf *bpf, void __iomem *value,
        return 0;
 }
 
+static int
+nfp_bpf_parse_cap_qsel(struct nfp_app_bpf *bpf, void __iomem *value, u32 length)
+{
+       bpf->queue_select = true;
+       return 0;
+}
+
 static int nfp_bpf_parse_capabilities(struct nfp_app *app)
 {
        struct nfp_cpp *cpp = app->pf->cpp;
@@ -346,7 +353,7 @@ static int nfp_bpf_parse_capabilities(struct nfp_app *app)
                return PTR_ERR(mem) == -ENOENT ? 0 : PTR_ERR(mem);
 
        start = mem;
-       while (mem - start + 8 < nfp_cpp_area_size(area)) {
+       while (mem - start + 8 <= nfp_cpp_area_size(area)) {
                u8 __iomem *value;
                u32 type, length;
 
@@ -376,6 +383,10 @@ static int nfp_bpf_parse_capabilities(struct nfp_app *app)
                        if (nfp_bpf_parse_cap_random(app->priv, value, length))
                                goto err_release_free;
                        break;
+               case NFP_BPF_CAP_TYPE_QUEUE_SELECT:
+                       if (nfp_bpf_parse_cap_qsel(app->priv, value, length))
+                               goto err_release_free;
+                       break;
                default:
                        nfp_dbg(cpp, "unknown BPF capability: %d\n", type);
                        break;
index 8268237..8b14354 100644 (file)
@@ -82,10 +82,16 @@ enum static_regs {
 enum pkt_vec {
        PKT_VEC_PKT_LEN         = 0,
        PKT_VEC_PKT_PTR         = 2,
+       PKT_VEC_QSEL_SET        = 4,
+       PKT_VEC_QSEL_VAL        = 6,
 };
 
+#define PKT_VEL_QSEL_SET_BIT   4
+
 #define pv_len(np)     reg_lm(1, PKT_VEC_PKT_LEN)
 #define pv_ctm_ptr(np) reg_lm(1, PKT_VEC_PKT_PTR)
+#define pv_qsel_set(np)        reg_lm(1, PKT_VEC_QSEL_SET)
+#define pv_qsel_val(np)        reg_lm(1, PKT_VEC_QSEL_VAL)
 
 #define stack_reg(np)  reg_a(STATIC_REG_STACK)
 #define stack_imm(np)  imm_b(np)
@@ -139,6 +145,7 @@ enum pkt_vec {
  * @helpers.perf_event_output: output perf event to a ring buffer
  *
  * @pseudo_random:     FW initialized the pseudo-random machinery (CSRs)
+ * @queue_select:      BPF can set the RX queue ID in packet vector
  */
 struct nfp_app_bpf {
        struct nfp_app *app;
@@ -181,6 +188,7 @@ struct nfp_app_bpf {
        } helpers;
 
        bool pseudo_random;
+       bool queue_select;
 };
 
 enum nfp_bpf_map_use {
index e163f3c..844a9be 100644 (file)
@@ -467,6 +467,30 @@ nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
        return 0;
 }
 
+static int
+nfp_bpf_check_store(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
+                   struct bpf_verifier_env *env)
+{
+       const struct bpf_reg_state *reg = cur_regs(env) + meta->insn.dst_reg;
+
+       if (reg->type == PTR_TO_CTX) {
+               if (nfp_prog->type == BPF_PROG_TYPE_XDP) {
+                       /* XDP ctx accesses must be 4B in size */
+                       switch (meta->insn.off) {
+                       case offsetof(struct xdp_md, rx_queue_index):
+                               if (nfp_prog->bpf->queue_select)
+                                       goto exit_check_ptr;
+                               pr_vlog(env, "queue selection not supported by FW\n");
+                               return -EOPNOTSUPP;
+                       }
+               }
+               pr_vlog(env, "unsupported store to context field\n");
+               return -EOPNOTSUPP;
+       }
+exit_check_ptr:
+       return nfp_bpf_check_ptr(nfp_prog, meta, env, meta->insn.dst_reg);
+}
+
 static int
 nfp_bpf_check_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
                   struct bpf_verifier_env *env)
@@ -522,8 +546,8 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
                return nfp_bpf_check_ptr(nfp_prog, meta, env,
                                         meta->insn.src_reg);
        if (is_mbpf_store(meta))
-               return nfp_bpf_check_ptr(nfp_prog, meta, env,
-                                        meta->insn.dst_reg);
+               return nfp_bpf_check_store(nfp_prog, meta, env);
+
        if (is_mbpf_xadd(meta))
                return nfp_bpf_check_xadd(nfp_prog, meta, env);
 
index 84e3b9f..4e67c0c 100644 (file)
@@ -247,12 +247,16 @@ nfp_flower_spawn_vnic_reprs(struct nfp_app *app,
                        err = -ENOMEM;
                        goto err_reprs_clean;
                }
-               RCU_INIT_POINTER(reprs->reprs[i], repr);
 
                /* For now we only support 1 PF */
                WARN_ON(repr_type == NFP_REPR_TYPE_PF && i);
 
                port = nfp_port_alloc(app, port_type, repr);
+               if (IS_ERR(port)) {
+                       err = PTR_ERR(port);
+                       nfp_repr_free(repr);
+                       goto err_reprs_clean;
+               }
                if (repr_type == NFP_REPR_TYPE_PF) {
                        port->pf_id = i;
                        port->vnic = priv->nn->dp.ctrl_bar;
@@ -271,9 +275,11 @@ nfp_flower_spawn_vnic_reprs(struct nfp_app *app,
                                    port_id, port, priv->nn->dp.netdev);
                if (err) {
                        nfp_port_free(port);
+                       nfp_repr_free(repr);
                        goto err_reprs_clean;
                }
 
+               RCU_INIT_POINTER(reprs->reprs[i], repr);
                nfp_info(app->cpp, "%s%d Representor(%s) created\n",
                         repr_type == NFP_REPR_TYPE_PF ? "PF" : "VF", i,
                         repr->name);
@@ -344,16 +350,17 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv)
                        err = -ENOMEM;
                        goto err_reprs_clean;
                }
-               RCU_INIT_POINTER(reprs->reprs[phys_port], repr);
 
                port = nfp_port_alloc(app, NFP_PORT_PHYS_PORT, repr);
                if (IS_ERR(port)) {
                        err = PTR_ERR(port);
+                       nfp_repr_free(repr);
                        goto err_reprs_clean;
                }
                err = nfp_port_init_phy_port(app->pf, app, port, i);
                if (err) {
                        nfp_port_free(port);
+                       nfp_repr_free(repr);
                        goto err_reprs_clean;
                }
 
@@ -365,6 +372,7 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv)
                                    cmsg_port_id, port, priv->nn->dp.netdev);
                if (err) {
                        nfp_port_free(port);
+                       nfp_repr_free(repr);
                        goto err_reprs_clean;
                }
 
@@ -373,6 +381,7 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv)
                                             eth_tbl->ports[i].base,
                                             phys_port);
 
+               RCU_INIT_POINTER(reprs->reprs[phys_port], repr);
                nfp_info(app->cpp, "Phys Port %d Representor(%s) created\n",
                         phys_port, repr->name);
        }
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_abi.h b/drivers/net/ethernet/netronome/nfp/nfp_abi.h
new file mode 100644 (file)
index 0000000..7ffa6e6
--- /dev/null
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: (GPL-2.0 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.
+ *
+ * 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 __NFP_ABI__
+#define __NFP_ABI__ 1
+
+#include <linux/types.h>
+
+#define NFP_MBOX_SYM_NAME              "_abi_nfd_pf%u_mbox"
+#define NFP_MBOX_SYM_MIN_SIZE          16 /* When no data needed */
+
+#define NFP_MBOX_CMD           0x00
+#define NFP_MBOX_RET           0x04
+#define NFP_MBOX_DATA_LEN      0x08
+#define NFP_MBOX_RESERVED      0x0c
+#define NFP_MBOX_DATA          0x10
+
+/**
+ * enum nfp_mbox_cmd - PF mailbox commands
+ *
+ * @NFP_MBOX_NO_CMD:   null command
+ * Used to indicate previous command has finished.
+ *
+ * @NFP_MBOX_POOL_GET: get shared buffer pool info/config
+ * Input  - struct nfp_shared_buf_pool_id
+ * Output - struct nfp_shared_buf_pool_info_get
+ *
+ * @NFP_MBOX_POOL_SET: set shared buffer pool info/config
+ * Input  - struct nfp_shared_buf_pool_info_set
+ * Output - None
+ */
+enum nfp_mbox_cmd {
+       NFP_MBOX_NO_CMD                 = 0x00,
+
+       NFP_MBOX_POOL_GET               = 0x01,
+       NFP_MBOX_POOL_SET               = 0x02,
+};
+
+#define NFP_SHARED_BUF_COUNT_SYM_NAME  "_abi_nfd_pf%u_sb_cnt"
+#define NFP_SHARED_BUF_TABLE_SYM_NAME  "_abi_nfd_pf%u_sb_tbl"
+
+/**
+ * struct nfp_shared_buf - NFP shared buffer description
+ * @id:                                numerical user-visible id of the shared buffer
+ * @size:                      size in bytes of the buffer
+ * @ingress_pools_count:       number of ingress pools
+ * @egress_pools_count:                number of egress pools
+ * @ingress_tc_count:          number of ingress trafic classes
+ * @egress_tc_count:           number of egress trafic classes
+ * @pool_size_unit:            pool size may be in credits, each credit is
+ *                             @pool_size_unit bytes
+ */
+struct nfp_shared_buf {
+       __le32 id;
+       __le32 size;
+       __le16 ingress_pools_count;
+       __le16 egress_pools_count;
+       __le16 ingress_tc_count;
+       __le16 egress_tc_count;
+
+       __le32 pool_size_unit;
+};
+
+/**
+ * struct nfp_shared_buf_pool_id - shared buffer pool identification
+ * @shared_buf:                shared buffer id
+ * @pool:              pool index
+ */
+struct nfp_shared_buf_pool_id {
+       __le32 shared_buf;
+       __le32 pool;
+};
+
+/**
+ * struct nfp_shared_buf_pool_info_get - struct devlink_sb_pool_info mirror
+ * @pool_type:         one of enum devlink_sb_pool_type
+ * @size:              pool size in units of SB's @pool_size_unit
+ * @threshold_type:    one of enum devlink_sb_threshold_type
+ */
+struct nfp_shared_buf_pool_info_get {
+       __le32 pool_type;
+       __le32 size;
+       __le32 threshold_type;
+};
+
+/**
+ * struct nfp_shared_buf_pool_info_set - packed args of sb_pool_set
+ * @id:                        pool identification info
+ * @size:              pool size in units of SB's @pool_size_unit
+ * @threshold_type:    one of enum devlink_sb_threshold_type
+ */
+struct nfp_shared_buf_pool_info_set {
+       struct nfp_shared_buf_pool_id id;
+       __le32 size;
+       __le32 threshold_type;
+};
+
+#endif
index 0e0253c..c9d8a7a 100644 (file)
@@ -54,6 +54,9 @@ static const struct nfp_app_type *apps[] = {
 #ifdef CONFIG_NFP_APP_FLOWER
        [NFP_APP_FLOWER_NIC]    = &app_flower,
 #endif
+#ifdef CONFIG_NFP_APP_ABM_NIC
+       [NFP_APP_ACTIVE_BUFFER_MGMT_NIC] = &app_abm,
+#endif
 };
 
 struct nfp_app *nfp_app_from_netdev(struct net_device *netdev)
index 2d9cb25..23b99a4 100644 (file)
@@ -57,11 +57,13 @@ enum nfp_app_id {
        NFP_APP_CORE_NIC        = 0x1,
        NFP_APP_BPF_NIC         = 0x2,
        NFP_APP_FLOWER_NIC      = 0x3,
+       NFP_APP_ACTIVE_BUFFER_MGMT_NIC = 0x4,
 };
 
 extern const struct nfp_app_type app_nic;
 extern const struct nfp_app_type app_bpf;
 extern const struct nfp_app_type app_flower;
+extern const struct nfp_app_type app_abm;
 
 /**
  * struct nfp_app_type - application definition
@@ -95,6 +97,7 @@ extern const struct nfp_app_type app_flower;
  * @bpf:       BPF ndo offload-related calls
  * @xdp_offload:    offload an XDP program
  * @eswitch_mode_get:    get SR-IOV eswitch mode
+ * @eswitch_mode_set:    set SR-IOV eswitch mode (under pf->lock)
  * @sriov_enable: app-specific sriov initialisation
  * @sriov_disable: app-specific sriov clean-up
  * @repr_get:  get representor netdev
@@ -146,6 +149,7 @@ struct nfp_app_type {
        void (*sriov_disable)(struct nfp_app *app);
 
        enum devlink_eswitch_mode (*eswitch_mode_get)(struct nfp_app *app);
+       int (*eswitch_mode_set)(struct nfp_app *app, u16 mode);
        struct net_device *(*repr_get)(struct nfp_app *app, u32 id);
 };
 
@@ -370,6 +374,13 @@ static inline int nfp_app_eswitch_mode_get(struct nfp_app *app, u16 *mode)
        return 0;
 }
 
+static inline int nfp_app_eswitch_mode_set(struct nfp_app *app, u16 mode)
+{
+       if (!app->type->eswitch_mode_set)
+               return -EOPNOTSUPP;
+       return app->type->eswitch_mode_set(app, mode);
+}
+
 static inline int nfp_app_sriov_enable(struct nfp_app *app, int num_vfs)
 {
        if (!app || !app->type->sriov_enable)
@@ -410,5 +421,7 @@ void nfp_app_free(struct nfp_app *app);
 
 int nfp_app_nic_vnic_alloc(struct nfp_app *app, struct nfp_net *nn,
                           unsigned int id);
+int nfp_app_nic_vnic_init_phy_port(struct nfp_pf *pf, struct nfp_app *app,
+                                  struct nfp_net *nn, unsigned int id);
 
 #endif
index b9618c3..e2dfe4f 100644 (file)
@@ -38,9 +38,8 @@
 #include "nfp_net.h"
 #include "nfp_port.h"
 
-static int
-nfp_app_nic_vnic_init_phy_port(struct nfp_pf *pf, struct nfp_app *app,
-                              struct nfp_net *nn, unsigned int id)
+int nfp_app_nic_vnic_init_phy_port(struct nfp_pf *pf, struct nfp_app *app,
+                                  struct nfp_net *nn, unsigned int id)
 {
        int err;
 
index 5f2b2f2..faa4e13 100644 (file)
@@ -183,16 +183,18 @@ enum shf_sc {
 #define OP_ALU_DST_LMEXTN      0x80000000000ULL
 
 enum alu_op {
-       ALU_OP_NONE     = 0x00,
-       ALU_OP_ADD      = 0x01,
-       ALU_OP_NOT      = 0x04,
-       ALU_OP_ADD_2B   = 0x05,
-       ALU_OP_AND      = 0x08,
-       ALU_OP_SUB_C    = 0x0d,
-       ALU_OP_ADD_C    = 0x11,
-       ALU_OP_OR       = 0x14,
-       ALU_OP_SUB      = 0x15,
-       ALU_OP_XOR      = 0x18,
+       ALU_OP_NONE             = 0x00,
+       ALU_OP_ADD              = 0x01,
+       ALU_OP_NOT              = 0x04,
+       ALU_OP_ADD_2B           = 0x05,
+       ALU_OP_AND              = 0x08,
+       ALU_OP_AND_NOT_A        = 0x0c,
+       ALU_OP_SUB_C            = 0x0d,
+       ALU_OP_AND_NOT_B        = 0x10,
+       ALU_OP_ADD_C            = 0x11,
+       ALU_OP_OR               = 0x14,
+       ALU_OP_SUB              = 0x15,
+       ALU_OP_XOR              = 0x18,
 };
 
 enum alu_dst_ab {
index eb0fc61..71c2edd 100644 (file)
@@ -149,6 +149,26 @@ out:
        return ret;
 }
 
+static int
+nfp_devlink_sb_pool_get(struct devlink *devlink, unsigned int sb_index,
+                       u16 pool_index, struct devlink_sb_pool_info *pool_info)
+{
+       struct nfp_pf *pf = devlink_priv(devlink);
+
+       return nfp_shared_buf_pool_get(pf, sb_index, pool_index, pool_info);
+}
+
+static int
+nfp_devlink_sb_pool_set(struct devlink *devlink, unsigned int sb_index,
+                       u16 pool_index,
+                       u32 size, enum devlink_sb_threshold_type threshold_type)
+{
+       struct nfp_pf *pf = devlink_priv(devlink);
+
+       return nfp_shared_buf_pool_set(pf, sb_index, pool_index,
+                                      size, threshold_type);
+}
+
 static int nfp_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
 {
        struct nfp_pf *pf = devlink_priv(devlink);
@@ -156,10 +176,25 @@ static int nfp_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
        return nfp_app_eswitch_mode_get(pf->app, mode);
 }
 
+static int nfp_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode)
+{
+       struct nfp_pf *pf = devlink_priv(devlink);
+       int ret;
+
+       mutex_lock(&pf->lock);
+       ret = nfp_app_eswitch_mode_set(pf->app, mode);
+       mutex_unlock(&pf->lock);
+
+       return ret;
+}
+
 const struct devlink_ops nfp_devlink_ops = {
        .port_split             = nfp_devlink_port_split,
        .port_unsplit           = nfp_devlink_port_unsplit,
+       .sb_pool_get            = nfp_devlink_sb_pool_get,
+       .sb_pool_set            = nfp_devlink_sb_pool_set,
        .eswitch_mode_get       = nfp_devlink_eswitch_mode_get,
+       .eswitch_mode_set       = nfp_devlink_eswitch_mode_set,
 };
 
 int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port)
@@ -175,8 +210,9 @@ int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port)
                return ret;
 
        devlink_port_type_eth_set(&port->dl_port, port->netdev);
-       if (eth_port.is_split)
-               devlink_port_split_set(&port->dl_port, eth_port.label_port);
+       devlink_port_attrs_set(&port->dl_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
+                              eth_port.label_port, eth_port.is_split,
+                              eth_port.label_subport);
 
        devlink = priv_to_devlink(app->pf);
 
index 0ade122..46b76d5 100644 (file)
@@ -55,6 +55,7 @@
 
 #include "nfpcore/nfp6000_pcie.h"
 
+#include "nfp_abi.h"
 #include "nfp_app.h"
 #include "nfp_main.h"
 #include "nfp_net.h"
@@ -75,6 +76,122 @@ static const struct pci_device_id nfp_pci_device_ids[] = {
 };
 MODULE_DEVICE_TABLE(pci, nfp_pci_device_ids);
 
+int nfp_pf_rtsym_read_optional(struct nfp_pf *pf, const char *format,
+                              unsigned int default_val)
+{
+       char name[256];
+       int err = 0;
+       u64 val;
+
+       snprintf(name, sizeof(name), format, nfp_cppcore_pcie_unit(pf->cpp));
+
+       val = nfp_rtsym_read_le(pf->rtbl, name, &err);
+       if (err) {
+               if (err == -ENOENT)
+                       return default_val;
+               nfp_err(pf->cpp, "Unable to read symbol %s\n", name);
+               return err;
+       }
+
+       return val;
+}
+
+u8 __iomem *
+nfp_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt,
+                unsigned int min_size, struct nfp_cpp_area **area)
+{
+       char pf_symbol[256];
+
+       snprintf(pf_symbol, sizeof(pf_symbol), sym_fmt,
+                nfp_cppcore_pcie_unit(pf->cpp));
+
+       return nfp_rtsym_map(pf->rtbl, pf_symbol, name, min_size, area);
+}
+
+/* Callers should hold the devlink instance lock */
+int nfp_mbox_cmd(struct nfp_pf *pf, u32 cmd, void *in_data, u64 in_length,
+                void *out_data, u64 out_length)
+{
+       unsigned long long addr;
+       unsigned long err_at;
+       u64 max_data_sz;
+       u32 val = 0;
+       u32 cpp_id;
+       int n, err;
+
+       if (!pf->mbox)
+               return -EOPNOTSUPP;
+
+       cpp_id = NFP_CPP_ISLAND_ID(pf->mbox->target, NFP_CPP_ACTION_RW, 0,
+                                  pf->mbox->domain);
+       addr = pf->mbox->addr;
+       max_data_sz = pf->mbox->size - NFP_MBOX_SYM_MIN_SIZE;
+
+       /* Check if cmd field is clear */
+       err = nfp_cpp_readl(pf->cpp, cpp_id, addr + NFP_MBOX_CMD, &val);
+       if (err || val) {
+               nfp_warn(pf->cpp, "failed to issue command (%u): %u, err: %d\n",
+                        cmd, val, err);
+               return err ?: -EBUSY;
+       }
+
+       in_length = min(in_length, max_data_sz);
+       n = nfp_cpp_write(pf->cpp, cpp_id, addr + NFP_MBOX_DATA,
+                         in_data, in_length);
+       if (n != in_length)
+               return -EIO;
+       /* Write data_len and wipe reserved */
+       err = nfp_cpp_writeq(pf->cpp, cpp_id, addr + NFP_MBOX_DATA_LEN,
+                            in_length);
+       if (err)
+               return err;
+
+       /* Read back for ordering */
+       err = nfp_cpp_readl(pf->cpp, cpp_id, addr + NFP_MBOX_DATA_LEN, &val);
+       if (err)
+               return err;
+
+       /* Write cmd and wipe return value */
+       err = nfp_cpp_writeq(pf->cpp, cpp_id, addr + NFP_MBOX_CMD, cmd);
+       if (err)
+               return err;
+
+       err_at = jiffies + 5 * HZ;
+       while (true) {
+               /* Wait for command to go to 0 (NFP_MBOX_NO_CMD) */
+               err = nfp_cpp_readl(pf->cpp, cpp_id, addr + NFP_MBOX_CMD, &val);
+               if (err)
+                       return err;
+               if (!val)
+                       break;
+
+               if (time_is_before_eq_jiffies(err_at))
+                       return -ETIMEDOUT;
+
+               msleep(5);
+       }
+
+       /* Copy output if any (could be error info, do it before reading ret) */
+       err = nfp_cpp_readl(pf->cpp, cpp_id, addr + NFP_MBOX_DATA_LEN, &val);
+       if (err)
+               return err;
+
+       out_length = min_t(u32, val, min(out_length, max_data_sz));
+       n = nfp_cpp_read(pf->cpp, cpp_id, addr + NFP_MBOX_DATA,
+                        out_data, out_length);
+       if (n != out_length)
+               return -EIO;
+
+       /* Check if there is an error */
+       err = nfp_cpp_readl(pf->cpp, cpp_id, addr + NFP_MBOX_RET, &val);
+       if (err)
+               return err;
+       if (val)
+               return -val;
+
+       return out_length;
+}
+
 static bool nfp_board_ready(struct nfp_pf *pf)
 {
        const char *cp;
@@ -436,6 +553,25 @@ static void nfp_fw_unload(struct nfp_pf *pf)
        nfp_nsp_close(nsp);
 }
 
+static int nfp_pf_find_rtsyms(struct nfp_pf *pf)
+{
+       char pf_symbol[256];
+       unsigned int pf_id;
+
+       pf_id = nfp_cppcore_pcie_unit(pf->cpp);
+
+       /* Optional per-PCI PF mailbox */
+       snprintf(pf_symbol, sizeof(pf_symbol), NFP_MBOX_SYM_NAME, pf_id);
+       pf->mbox = nfp_rtsym_lookup(pf->rtbl, pf_symbol);
+       if (pf->mbox && pf->mbox->size < NFP_MBOX_SYM_MIN_SIZE) {
+               nfp_err(pf->cpp, "PF mailbox symbol too small: %llu < %d\n",
+                       pf->mbox->size, NFP_MBOX_SYM_MIN_SIZE);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int nfp_pci_probe(struct pci_dev *pdev,
                         const struct pci_device_id *pci_id)
 {
@@ -510,6 +646,10 @@ static int nfp_pci_probe(struct pci_dev *pdev,
        pf->mip = nfp_mip_open(pf->cpp);
        pf->rtbl = __nfp_rtsym_table_read(pf->cpp, pf->mip);
 
+       err = nfp_pf_find_rtsyms(pf);
+       if (err)
+               goto err_fw_unload;
+
        pf->dump_flag = NFP_DUMP_NSP_DIAG;
        pf->dumpspec = nfp_net_dump_load_dumpspec(pf->cpp, pf->rtbl);
 
index 4221108..595b3dc 100644 (file)
 #include <linux/mutex.h>
 #include <linux/pci.h>
 #include <linux/workqueue.h>
+#include <net/devlink.h>
 
 struct dentry;
 struct device;
-struct devlink_ops;
 struct pci_dev;
 
 struct nfp_cpp;
@@ -60,7 +60,9 @@ struct nfp_mip;
 struct nfp_net;
 struct nfp_nsp_identify;
 struct nfp_port;
+struct nfp_rtsym;
 struct nfp_rtsym_table;
+struct nfp_shared_buf;
 
 /**
  * struct nfp_dumpspec - NFP FW dump specification structure
@@ -87,6 +89,7 @@ struct nfp_dumpspec {
  * @vf_cfg_mem:                Pointer to mapped VF configuration area
  * @vfcfg_tbl2_area:   Pointer to the CPP area for the VF config table
  * @vfcfg_tbl2:                Pointer to mapped VF config table
+ * @mbox:              RTSym of per-PCI PF mailbox (under devlink lock)
  * @irq_entries:       Array of MSI-X entries for all vNICs
  * @limit_vfs:         Number of VFs supported by firmware (~0 for PCI limit)
  * @num_vfs:           Number of SR-IOV VFs enabled
@@ -108,6 +111,8 @@ struct nfp_dumpspec {
  * @ports:             Linked list of port structures (struct nfp_port)
  * @wq:                        Workqueue for running works which need to grab @lock
  * @port_refresh_work: Work entry for taking netdevs out
+ * @shared_bufs:       Array of shared buffer structures if FW has any SBs
+ * @num_shared_bufs:   Number of elements in @shared_bufs
  * @lock:              Protects all fields which may change after probe
  */
 struct nfp_pf {
@@ -127,6 +132,8 @@ struct nfp_pf {
        struct nfp_cpp_area *vfcfg_tbl2_area;
        u8 __iomem *vfcfg_tbl2;
 
+       const struct nfp_rtsym *mbox;
+
        struct msix_entry *irq_entries;
 
        unsigned int limit_vfs;
@@ -158,6 +165,9 @@ struct nfp_pf {
        struct workqueue_struct *wq;
        struct work_struct port_refresh_work;
 
+       struct nfp_shared_buf *shared_bufs;
+       unsigned int num_shared_bufs;
+
        struct mutex lock;
 };
 
@@ -177,6 +187,14 @@ nfp_net_get_mac_addr(struct nfp_pf *pf, struct net_device *netdev,
 
 bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb);
 
+int nfp_pf_rtsym_read_optional(struct nfp_pf *pf, const char *format,
+                              unsigned int default_val);
+u8 __iomem *
+nfp_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt,
+                unsigned int min_size, struct nfp_cpp_area **area);
+int nfp_mbox_cmd(struct nfp_pf *pf, u32 cmd, void *in_data, u64 in_length,
+                void *out_data, u64 out_length);
+
 enum nfp_dump_diag {
        NFP_DUMP_NSP_DIAG = 0,
 };
@@ -188,4 +206,11 @@ s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec,
 int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec,
                                 struct ethtool_dump *dump_param, void *dest);
 
+int nfp_shared_buf_register(struct nfp_pf *pf);
+void nfp_shared_buf_unregister(struct nfp_pf *pf);
+int nfp_shared_buf_pool_get(struct nfp_pf *pf, unsigned int sb, u16 pool_index,
+                           struct devlink_sb_pool_info *pool_info);
+int nfp_shared_buf_pool_set(struct nfp_pf *pf, unsigned int sb,
+                           u16 pool_index, u32 size,
+                           enum devlink_sb_threshold_type threshold_type);
 #endif /* NFP_MAIN_H */
index bd7d8ae..57cb035 100644 (file)
@@ -545,6 +545,7 @@ struct nfp_net_dp {
 /**
  * struct nfp_net - NFP network device structure
  * @dp:                        Datapath structure
+ * @id:                        vNIC id within the PF (0 for VFs)
  * @fw_ver:            Firmware version
  * @cap:                Capabilities advertised by the Firmware
  * @max_mtu:            Maximum support MTU advertised by the Firmware
@@ -597,6 +598,8 @@ struct nfp_net {
 
        struct nfp_net_fw_version fw_ver;
 
+       u32 id;
+
        u32 cap;
        u32 max_mtu;
 
@@ -909,7 +912,7 @@ int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new,
 void nfp_net_debugfs_create(void);
 void nfp_net_debugfs_destroy(void);
 struct dentry *nfp_net_debugfs_device_add(struct pci_dev *pdev);
-void nfp_net_debugfs_vnic_add(struct nfp_net *nn, struct dentry *ddir, int id);
+void nfp_net_debugfs_vnic_add(struct nfp_net *nn, struct dentry *ddir);
 void nfp_net_debugfs_dir_clean(struct dentry **dir);
 #else
 static inline void nfp_net_debugfs_create(void)
@@ -926,7 +929,7 @@ static inline struct dentry *nfp_net_debugfs_device_add(struct pci_dev *pdev)
 }
 
 static inline void
-nfp_net_debugfs_vnic_add(struct nfp_net *nn, struct dentry *ddir, int id)
+nfp_net_debugfs_vnic_add(struct nfp_net *nn, struct dentry *ddir)
 {
 }
 
index d9111c0..eea11e8 100644 (file)
@@ -3277,6 +3277,24 @@ nfp_net_features_check(struct sk_buff *skb, struct net_device *dev,
        return features;
 }
 
+static int
+nfp_net_get_phys_port_name(struct net_device *netdev, char *name, size_t len)
+{
+       struct nfp_net *nn = netdev_priv(netdev);
+       int n;
+
+       if (nn->port)
+               return nfp_port_get_phys_port_name(netdev, name, len);
+
+       if (!nn->dp.is_vf) {
+               n = snprintf(name, len, "%d", nn->id);
+               if (n >= len)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
 /**
  * nfp_net_set_vxlan_port() - set vxlan port in SW and reconfigure HW
  * @nn:   NFP Net device to reconfigure
@@ -3475,7 +3493,7 @@ const struct net_device_ops nfp_net_netdev_ops = {
        .ndo_set_mac_address    = nfp_net_set_mac_address,
        .ndo_set_features       = nfp_net_set_features,
        .ndo_features_check     = nfp_net_features_check,
-       .ndo_get_phys_port_name = nfp_port_get_phys_port_name,
+       .ndo_get_phys_port_name = nfp_net_get_phys_port_name,
        .ndo_udp_tunnel_add     = nfp_net_add_vxlan_port,
        .ndo_udp_tunnel_del     = nfp_net_del_vxlan_port,
        .ndo_bpf                = nfp_net_xdp,
index 67cdd83..099b63d 100644 (file)
@@ -201,7 +201,7 @@ static const struct file_operations nfp_xdp_q_fops = {
        .llseek = seq_lseek
 };
 
-void nfp_net_debugfs_vnic_add(struct nfp_net *nn, struct dentry *ddir, int id)
+void nfp_net_debugfs_vnic_add(struct nfp_net *nn, struct dentry *ddir)
 {
        struct dentry *queues, *tx, *rx, *xdp;
        char name[20];
@@ -211,7 +211,7 @@ void nfp_net_debugfs_vnic_add(struct nfp_net *nn, struct dentry *ddir, int id)
                return;
 
        if (nfp_net_is_data_vnic(nn))
-               sprintf(name, "vnic%d", id);
+               sprintf(name, "vnic%d", nn->id);
        else
                strcpy(name, "ctrl-vnic");
        nn->debugfs_dir = debugfs_create_dir(name, ddir);
index 45cd209..28516ee 100644 (file)
@@ -101,48 +101,15 @@ nfp_net_find_port(struct nfp_eth_table *eth_tbl, unsigned int index)
        return NULL;
 }
 
-static int
-nfp_net_pf_rtsym_read_optional(struct nfp_pf *pf, const char *format,
-                              unsigned int default_val)
-{
-       char name[256];
-       int err = 0;
-       u64 val;
-
-       snprintf(name, sizeof(name), format, nfp_cppcore_pcie_unit(pf->cpp));
-
-       val = nfp_rtsym_read_le(pf->rtbl, name, &err);
-       if (err) {
-               if (err == -ENOENT)
-                       return default_val;
-               nfp_err(pf->cpp, "Unable to read symbol %s\n", name);
-               return err;
-       }
-
-       return val;
-}
-
 static int nfp_net_pf_get_num_ports(struct nfp_pf *pf)
 {
-       return nfp_net_pf_rtsym_read_optional(pf, "nfd_cfg_pf%u_num_ports", 1);
+       return nfp_pf_rtsym_read_optional(pf, "nfd_cfg_pf%u_num_ports", 1);
 }
 
 static int nfp_net_pf_get_app_id(struct nfp_pf *pf)
 {
-       return nfp_net_pf_rtsym_read_optional(pf, "_pf%u_net_app_id",
-                                             NFP_APP_CORE_NIC);
-}
-
-static u8 __iomem *
-nfp_net_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt,
-                    unsigned int min_size, struct nfp_cpp_area **area)
-{
-       char pf_symbol[256];
-
-       snprintf(pf_symbol, sizeof(pf_symbol), sym_fmt,
-                nfp_cppcore_pcie_unit(pf->cpp));
-
-       return nfp_rtsym_map(pf->rtbl, pf_symbol, name, min_size, area);
+       return nfp_pf_rtsym_read_optional(pf, "_pf%u_net_app_id",
+                                         NFP_APP_CORE_NIC);
 }
 
 static void nfp_net_pf_free_vnic(struct nfp_pf *pf, struct nfp_net *nn)
@@ -211,11 +178,13 @@ nfp_net_pf_init_vnic(struct nfp_pf *pf, struct nfp_net *nn, unsigned int id)
 {
        int err;
 
+       nn->id = id;
+
        err = nfp_net_init(nn);
        if (err)
                return err;
 
-       nfp_net_debugfs_vnic_add(nn, pf->ddir, id);
+       nfp_net_debugfs_vnic_add(nn, pf->ddir);
 
        if (nn->port) {
                err = nfp_devlink_port_register(pf->app, nn->port);
@@ -379,9 +348,8 @@ nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride)
        if (!nfp_app_needs_ctrl_vnic(pf->app))
                return 0;
 
-       ctrl_bar = nfp_net_pf_map_rtsym(pf, "net.ctrl", "_pf%u_net_ctrl_bar",
-                                       NFP_PF_CSR_SLICE_SIZE,
-                                       &pf->ctrl_vnic_bar);
+       ctrl_bar = nfp_pf_map_rtsym(pf, "net.ctrl", "_pf%u_net_ctrl_bar",
+                                   NFP_PF_CSR_SLICE_SIZE, &pf->ctrl_vnic_bar);
        if (IS_ERR(ctrl_bar)) {
                nfp_err(pf->cpp, "Failed to find ctrl vNIC memory symbol\n");
                err = PTR_ERR(ctrl_bar);
@@ -507,8 +475,8 @@ static int nfp_net_pci_map_mem(struct nfp_pf *pf)
        int err;
 
        min_size = pf->max_data_vnics * NFP_PF_CSR_SLICE_SIZE;
-       mem = nfp_net_pf_map_rtsym(pf, "net.bar0", "_pf%d_net_bar0",
-                                  min_size, &pf->data_vnic_bar);
+       mem = nfp_pf_map_rtsym(pf, "net.bar0", "_pf%d_net_bar0",
+                              min_size, &pf->data_vnic_bar);
        if (IS_ERR(mem)) {
                nfp_err(pf->cpp, "Failed to find data vNIC memory symbol\n");
                return PTR_ERR(mem);
@@ -528,10 +496,9 @@ static int nfp_net_pci_map_mem(struct nfp_pf *pf)
                }
        }
 
-       pf->vf_cfg_mem = nfp_net_pf_map_rtsym(pf, "net.vfcfg",
-                                             "_pf%d_net_vf_bar",
-                                             NFP_NET_CFG_BAR_SZ *
-                                             pf->limit_vfs, &pf->vf_cfg_bar);
+       pf->vf_cfg_mem = nfp_pf_map_rtsym(pf, "net.vfcfg", "_pf%d_net_vf_bar",
+                                         NFP_NET_CFG_BAR_SZ * pf->limit_vfs,
+                                         &pf->vf_cfg_bar);
        if (IS_ERR(pf->vf_cfg_mem)) {
                if (PTR_ERR(pf->vf_cfg_mem) != -ENOENT) {
                        err = PTR_ERR(pf->vf_cfg_mem);
@@ -541,9 +508,9 @@ static int nfp_net_pci_map_mem(struct nfp_pf *pf)
        }
 
        min_size = NFP_NET_VF_CFG_SZ * pf->limit_vfs + NFP_NET_VF_CFG_MB_SZ;
-       pf->vfcfg_tbl2 = nfp_net_pf_map_rtsym(pf, "net.vfcfg_tbl2",
-                                             "_pf%d_net_vf_cfg2",
-                                             min_size, &pf->vfcfg_tbl2_area);
+       pf->vfcfg_tbl2 = nfp_pf_map_rtsym(pf, "net.vfcfg_tbl2",
+                                         "_pf%d_net_vf_cfg2",
+                                         min_size, &pf->vfcfg_tbl2_area);
        if (IS_ERR(pf->vfcfg_tbl2)) {
                if (PTR_ERR(pf->vfcfg_tbl2) != -ENOENT) {
                        err = PTR_ERR(pf->vfcfg_tbl2);
@@ -763,6 +730,10 @@ int nfp_net_pci_probe(struct nfp_pf *pf)
        if (err)
                goto err_app_clean;
 
+       err = nfp_shared_buf_register(pf);
+       if (err)
+               goto err_devlink_unreg;
+
        mutex_lock(&pf->lock);
        pf->ddir = nfp_net_debugfs_device_add(pf->pdev);
 
@@ -796,6 +767,8 @@ err_free_vnics:
 err_clean_ddir:
        nfp_net_debugfs_dir_clean(&pf->ddir);
        mutex_unlock(&pf->lock);
+       nfp_shared_buf_unregister(pf);
+err_devlink_unreg:
        cancel_work_sync(&pf->port_refresh_work);
        devlink_unregister(devlink);
 err_app_clean:
@@ -823,6 +796,7 @@ void nfp_net_pci_remove(struct nfp_pf *pf)
 
        mutex_unlock(&pf->lock);
 
+       nfp_shared_buf_unregister(pf);
        devlink_unregister(priv_to_devlink(pf));
 
        nfp_net_pf_free_irqs(pf);
index 0cd077a..09e87d5 100644 (file)
@@ -348,12 +348,17 @@ err_clean:
        return err;
 }
 
-static void nfp_repr_free(struct nfp_repr *repr)
+static void __nfp_repr_free(struct nfp_repr *repr)
 {
        free_percpu(repr->stats);
        free_netdev(repr->netdev);
 }
 
+void nfp_repr_free(struct net_device *netdev)
+{
+       __nfp_repr_free(netdev_priv(netdev));
+}
+
 struct net_device *nfp_repr_alloc(struct nfp_app *app)
 {
        struct net_device *netdev;
@@ -380,12 +385,12 @@ err_free_netdev:
        return NULL;
 }
 
-static void nfp_repr_clean_and_free(struct nfp_repr *repr)
+void nfp_repr_clean_and_free(struct nfp_repr *repr)
 {
        nfp_info(repr->app->cpp, "Destroying Representor(%s)\n",
                 repr->netdev->name);
        nfp_repr_clean(repr);
-       nfp_repr_free(repr);
+       __nfp_repr_free(repr);
 }
 
 void nfp_reprs_clean_and_free(struct nfp_app *app, struct nfp_reprs *reprs)
index a621e8f..8366e4f 100644 (file)
@@ -76,6 +76,7 @@ struct nfp_repr_pcpu_stats {
  * @port:      Port of representor
  * @app:       APP handle
  * @stats:     Statistic of packets hitting CPU
+ * @app_priv:  Pointer for APP data
  */
 struct nfp_repr {
        struct net_device *netdev;
@@ -83,6 +84,7 @@ struct nfp_repr {
        struct nfp_port *port;
        struct nfp_app *app;
        struct nfp_repr_pcpu_stats __percpu *stats;
+       void *app_priv;
 };
 
 /**
@@ -123,7 +125,9 @@ void nfp_repr_inc_rx_stats(struct net_device *netdev, unsigned int len);
 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);
+void nfp_repr_free(struct net_device *netdev);
 struct net_device *nfp_repr_alloc(struct nfp_app *app);
+void nfp_repr_clean_and_free(struct nfp_repr *repr);
 void nfp_reprs_clean_and_free(struct nfp_app *app, struct nfp_reprs *reprs);
 void nfp_reprs_clean_and_free_by_type(struct nfp_app *app,
                                      enum nfp_repr_type type);
index b802a1d..68928c8 100644 (file)
@@ -283,7 +283,7 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev,
 
        nfp_net_info(nn);
        vf->ddir = nfp_net_debugfs_device_add(pdev);
-       nfp_net_debugfs_vnic_add(nn, vf->ddir, 0);
+       nfp_net_debugfs_vnic_add(nn, vf->ddir);
 
        return 0;
 
index 7bd8be5..9c12981 100644 (file)
@@ -181,7 +181,11 @@ nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len)
                                     eth_port->label_subport);
                break;
        case NFP_PORT_PF_PORT:
-               n = snprintf(name, len, "pf%d", port->pf_id);
+               if (!port->pf_split)
+                       n = snprintf(name, len, "pf%d", port->pf_id);
+               else
+                       n = snprintf(name, len, "pf%ds%d", port->pf_id,
+                                    port->pf_split_id);
                break;
        case NFP_PORT_VF_PORT:
                n = snprintf(name, len, "pf%dvf%d", port->pf_id, port->vf_id);
@@ -218,6 +222,8 @@ int nfp_port_configure(struct net_device *netdev, bool configed)
        eth_port = __nfp_port_get_eth_port(port);
        if (!eth_port)
                return 0;
+       if (port->eth_forced)
+               return 0;
 
        err = nfp_eth_set_configured(port->app->cpp, eth_port->index, configed);
        return err < 0 && err != -EOPNOTSUPP ? err : 0;
index fa7e669..1866675 100644 (file)
@@ -77,10 +77,13 @@ enum nfp_port_flags {
  * @app:       backpointer to the app structure
  * @dl_port:   devlink port structure
  * @eth_id:    for %NFP_PORT_PHYS_PORT port ID in NFP enumeration scheme
+ * @eth_forced:        for %NFP_PORT_PHYS_PORT port is forced UP or DOWN, don't change
  * @eth_port:  for %NFP_PORT_PHYS_PORT translated ETH Table port entry
  * @eth_stats: for %NFP_PORT_PHYS_PORT MAC stats if available
  * @pf_id:     for %NFP_PORT_PF_PORT, %NFP_PORT_VF_PORT ID of the PCI PF (0-3)
  * @vf_id:     for %NFP_PORT_VF_PORT ID of the PCI VF within @pf_id
+ * @pf_split:  for %NFP_PORT_PF_PORT %true if PCI PF has more than one vNIC
+ * @pf_split_id:for %NFP_PORT_PF_PORT ID of PCI PF vNIC (valid if @pf_split)
  * @vnic:      for %NFP_PORT_PF_PORT, %NFP_PORT_VF_PORT vNIC ctrl memory
  * @port_list: entry on pf's list of ports
  */
@@ -99,6 +102,7 @@ struct nfp_port {
                /* NFP_PORT_PHYS_PORT */
                struct {
                        unsigned int eth_id;
+                       bool eth_forced;
                        struct nfp_eth_table_port *eth_port;
                        u8 __iomem *eth_stats;
                };
@@ -106,6 +110,8 @@ struct nfp_port {
                struct {
                        unsigned int pf_id;
                        unsigned int vf_id;
+                       bool pf_split;
+                       unsigned int pf_split_id;
                        u8 __iomem *vnic;
                };
        };
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_shared_buf.c b/drivers/net/ethernet/netronome/nfp/nfp_shared_buf.c
new file mode 100644 (file)
index 0000000..0ecd837
--- /dev/null
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: (GPL-2.0 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.
+ *
+ * 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 <net/devlink.h>
+
+#include "nfpcore/nfp_cpp.h"
+#include "nfpcore/nfp_nffw.h"
+#include "nfp_abi.h"
+#include "nfp_app.h"
+#include "nfp_main.h"
+
+static u32 nfp_shared_buf_pool_unit(struct nfp_pf *pf, unsigned int sb)
+{
+       __le32 sb_id = cpu_to_le32(sb);
+       unsigned int i;
+
+       for (i = 0; i < pf->num_shared_bufs; i++)
+               if (pf->shared_bufs[i].id == sb_id)
+                       return le32_to_cpu(pf->shared_bufs[i].pool_size_unit);
+
+       WARN_ON_ONCE(1);
+       return 0;
+}
+
+int nfp_shared_buf_pool_get(struct nfp_pf *pf, unsigned int sb, u16 pool_index,
+                           struct devlink_sb_pool_info *pool_info)
+{
+       struct nfp_shared_buf_pool_info_get get_data;
+       struct nfp_shared_buf_pool_id id = {
+               .shared_buf     = cpu_to_le32(sb),
+               .pool           = cpu_to_le32(pool_index),
+       };
+       unsigned int unit_size;
+       int n;
+
+       unit_size = nfp_shared_buf_pool_unit(pf, sb);
+       if (!unit_size)
+               return -EINVAL;
+
+       n = nfp_mbox_cmd(pf, NFP_MBOX_POOL_GET, &id, sizeof(id),
+                        &get_data, sizeof(get_data));
+       if (n < 0)
+               return n;
+       if (n < sizeof(get_data))
+               return -EIO;
+
+       pool_info->pool_type = le32_to_cpu(get_data.pool_type);
+       pool_info->threshold_type = le32_to_cpu(get_data.threshold_type);
+       pool_info->size = le32_to_cpu(get_data.size) * unit_size;
+
+       return 0;
+}
+
+int nfp_shared_buf_pool_set(struct nfp_pf *pf, unsigned int sb,
+                           u16 pool_index, u32 size,
+                           enum devlink_sb_threshold_type threshold_type)
+{
+       struct nfp_shared_buf_pool_info_set set_data = {
+               .id = {
+                       .shared_buf     = cpu_to_le32(sb),
+                       .pool           = cpu_to_le32(pool_index),
+               },
+               .threshold_type = cpu_to_le32(threshold_type),
+       };
+       unsigned int unit_size;
+
+       unit_size = nfp_shared_buf_pool_unit(pf, sb);
+       if (!unit_size || size % unit_size)
+               return -EINVAL;
+       set_data.size = cpu_to_le32(size / unit_size);
+
+       return nfp_mbox_cmd(pf, NFP_MBOX_POOL_SET, &set_data, sizeof(set_data),
+                           NULL, 0);
+}
+
+int nfp_shared_buf_register(struct nfp_pf *pf)
+{
+       struct devlink *devlink = priv_to_devlink(pf);
+       unsigned int i, num_entries, entry_sz;
+       struct nfp_cpp_area *sb_desc_area;
+       u8 __iomem *sb_desc;
+       int n, err;
+
+       if (!pf->mbox)
+               return 0;
+
+       n = nfp_pf_rtsym_read_optional(pf, NFP_SHARED_BUF_COUNT_SYM_NAME, 0);
+       if (n <= 0)
+               return n;
+       num_entries = n;
+
+       sb_desc = nfp_pf_map_rtsym(pf, "sb_tbl", NFP_SHARED_BUF_TABLE_SYM_NAME,
+                                  num_entries * sizeof(pf->shared_bufs[0]),
+                                  &sb_desc_area);
+       if (IS_ERR(sb_desc))
+               return PTR_ERR(sb_desc);
+
+       entry_sz = nfp_cpp_area_size(sb_desc_area) / num_entries;
+
+       pf->shared_bufs = kmalloc_array(num_entries, sizeof(pf->shared_bufs[0]),
+                                       GFP_KERNEL);
+       if (!pf->shared_bufs) {
+               err = -ENOMEM;
+               goto err_release_area;
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               struct nfp_shared_buf *sb = &pf->shared_bufs[i];
+
+               /* Entries may be larger in future FW */
+               memcpy_fromio(sb, sb_desc + i * entry_sz, sizeof(*sb));
+
+               err = devlink_sb_register(devlink,
+                                         le32_to_cpu(sb->id),
+                                         le32_to_cpu(sb->size),
+                                         le16_to_cpu(sb->ingress_pools_count),
+                                         le16_to_cpu(sb->egress_pools_count),
+                                         le16_to_cpu(sb->ingress_tc_count),
+                                         le16_to_cpu(sb->egress_tc_count));
+               if (err)
+                       goto err_unreg_prev;
+       }
+       pf->num_shared_bufs = num_entries;
+
+       nfp_cpp_area_release_free(sb_desc_area);
+
+       return 0;
+
+err_unreg_prev:
+       while (i--)
+               devlink_sb_unregister(devlink,
+                                     le32_to_cpu(pf->shared_bufs[i].id));
+       kfree(pf->shared_bufs);
+err_release_area:
+       nfp_cpp_area_release_free(sb_desc_area);
+       return err;
+}
+
+void nfp_shared_buf_unregister(struct nfp_pf *pf)
+{
+       struct devlink *devlink = priv_to_devlink(pf);
+       unsigned int i;
+
+       for (i = 0; i < pf->num_shared_bufs; i++)
+               devlink_sb_unregister(devlink,
+                                     le32_to_cpu(pf->shared_bufs[i].id));
+       kfree(pf->shared_bufs);
+}
index a0e336b..749655c 100644 (file)
@@ -933,7 +933,6 @@ static int nfp6000_area_read(struct nfp_cpp_area *area, void *kernel_vaddr,
        u32 *wrptr32 = kernel_vaddr;
        const u32 __iomem *rdptr32;
        int n, width;
-       bool is_64;
 
        priv = nfp_cpp_area_priv(area);
        rdptr64 = priv->iomem + offset;
@@ -943,10 +942,15 @@ static int nfp6000_area_read(struct nfp_cpp_area *area, void *kernel_vaddr,
                return -EFAULT;
 
        width = priv->width.read;
-
        if (width <= 0)
                return -EINVAL;
 
+       /* MU reads via a PCIe2CPP BAR support 32bit (and other) lengths */
+       if (priv->target == (NFP_CPP_TARGET_MU & NFP_CPP_TARGET_ID_MASK) &&
+           priv->action == NFP_CPP_ACTION_RW &&
+           (offset % sizeof(u64) == 4 || length % sizeof(u64) == 4))
+               width = TARGET_WIDTH_32;
+
        /* Unaligned? Translate to an explicit access */
        if ((priv->offset + offset) & (width - 1))
                return nfp_cpp_explicit_read(nfp_cpp_area_cpp(area),
@@ -956,36 +960,29 @@ static int nfp6000_area_read(struct nfp_cpp_area *area, void *kernel_vaddr,
                                             priv->offset + offset,
                                             kernel_vaddr, length, width);
 
-       is_64 = width == TARGET_WIDTH_64;
-
-       /* MU reads via a PCIe2CPP BAR supports 32bit (and other) lengths */
-       if (priv->target == (NFP_CPP_TARGET_ID_MASK & NFP_CPP_TARGET_MU) &&
-           priv->action == NFP_CPP_ACTION_RW)
-               is_64 = false;
+       if (WARN_ON(!priv->bar))
+               return -EFAULT;
 
-       if (is_64) {
-               if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
-                       return -EINVAL;
-       } else {
+       switch (width) {
+       case TARGET_WIDTH_32:
                if (offset % sizeof(u32) != 0 || length % sizeof(u32) != 0)
                        return -EINVAL;
-       }
 
-       if (WARN_ON(!priv->bar))
-               return -EFAULT;
+               for (n = 0; n < length; n += sizeof(u32))
+                       *wrptr32++ = __raw_readl(rdptr32++);
+               return n;
+#ifdef __raw_readq
+       case TARGET_WIDTH_64:
+               if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
+                       return -EINVAL;
 
-       if (is_64)
-#ifndef __raw_readq
-               return -EINVAL;
-#else
                for (n = 0; n < length; n += sizeof(u64))
                        *wrptr64++ = __raw_readq(rdptr64++);
+               return n;
 #endif
-       else
-               for (n = 0; n < length; n += sizeof(u32))
-                       *wrptr32++ = __raw_readl(rdptr32++);
-
-       return n;
+       default:
+               return -EINVAL;
+       }
 }
 
 static int
@@ -999,7 +996,6 @@ nfp6000_area_write(struct nfp_cpp_area *area,
        struct nfp6000_area_priv *priv;
        u32 __iomem *wrptr32;
        int n, width;
-       bool is_64;
 
        priv = nfp_cpp_area_priv(area);
        wrptr64 = priv->iomem + offset;
@@ -1009,10 +1005,15 @@ nfp6000_area_write(struct nfp_cpp_area *area,
                return -EFAULT;
 
        width = priv->width.write;
-
        if (width <= 0)
                return -EINVAL;
 
+       /* MU writes via a PCIe2CPP BAR support 32bit (and other) lengths */
+       if (priv->target == (NFP_CPP_TARGET_ID_MASK & NFP_CPP_TARGET_MU) &&
+           priv->action == NFP_CPP_ACTION_RW &&
+           (offset % sizeof(u64) == 4 || length % sizeof(u64) == 4))
+               width = TARGET_WIDTH_32;
+
        /* Unaligned? Translate to an explicit access */
        if ((priv->offset + offset) & (width - 1))
                return nfp_cpp_explicit_write(nfp_cpp_area_cpp(area),
@@ -1022,40 +1023,33 @@ nfp6000_area_write(struct nfp_cpp_area *area,
                                              priv->offset + offset,
                                              kernel_vaddr, length, width);
 
-       is_64 = width == TARGET_WIDTH_64;
-
-       /* MU writes via a PCIe2CPP BAR supports 32bit (and other) lengths */
-       if (priv->target == (NFP_CPP_TARGET_ID_MASK & NFP_CPP_TARGET_MU) &&
-           priv->action == NFP_CPP_ACTION_RW)
-               is_64 = false;
+       if (WARN_ON(!priv->bar))
+               return -EFAULT;
 
-       if (is_64) {
-               if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
-                       return -EINVAL;
-       } else {
+       switch (width) {
+       case TARGET_WIDTH_32:
                if (offset % sizeof(u32) != 0 || length % sizeof(u32) != 0)
                        return -EINVAL;
-       }
 
-       if (WARN_ON(!priv->bar))
-               return -EFAULT;
+               for (n = 0; n < length; n += sizeof(u32)) {
+                       __raw_writel(*rdptr32++, wrptr32++);
+                       wmb();
+               }
+               return n;
+#ifdef __raw_writeq
+       case TARGET_WIDTH_64:
+               if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
+                       return -EINVAL;
 
-       if (is_64)
-#ifndef __raw_writeq
-               return -EINVAL;
-#else
                for (n = 0; n < length; n += sizeof(u64)) {
                        __raw_writeq(*rdptr64++, wrptr64++);
                        wmb();
                }
+               return n;
 #endif
-       else
-               for (n = 0; n < length; n += sizeof(u32)) {
-                       __raw_writel(*rdptr32++, wrptr32++);
-                       wmb();
-               }
-
-       return n;
+       default:
+               return -EINVAL;
+       }
 }
 
 struct nfp6000_explicit_priv {
index c70cf2a..a0acb94 100644 (file)
@@ -3,7 +3,7 @@ obj-$(CONFIG_QED) := qed.o
 
 qed-y := qed_cxt.o qed_dev.o qed_hw.o qed_init_fw_funcs.o qed_init_ops.o \
         qed_int.o qed_main.o qed_mcp.o qed_sp_commands.o qed_spq.o qed_l2.o \
-        qed_selftest.o qed_dcbx.o qed_debug.o qed_ptp.o
+        qed_selftest.o qed_dcbx.o qed_debug.o qed_ptp.o qed_mng_tlv.o
 qed-$(CONFIG_QED_SRIOV) += qed_sriov.o qed_vf.o
 qed-$(CONFIG_QED_LL2) += qed_ll2.o
 qed-$(CONFIG_QED_RDMA) += qed_roce.o qed_rdma.o qed_iwarp.o
index adcff49..00db340 100644 (file)
@@ -92,6 +92,8 @@ struct qed_eth_cb_ops;
 struct qed_dev_info;
 union qed_mcp_protocol_stats;
 enum qed_mcp_protocol_type;
+enum qed_mfw_tlv_type;
+union qed_mfw_tlv_data;
 
 /* helpers */
 #define QED_MFW_GET_FIELD(name, field) \
@@ -513,6 +515,10 @@ struct qed_simd_fp_handler {
        void    (*func)(void *);
 };
 
+enum qed_slowpath_wq_flag {
+       QED_SLOWPATH_MFW_TLV_REQ,
+};
+
 struct qed_hwfn {
        struct qed_dev                  *cdev;
        u8                              my_id;          /* ID inside the PF */
@@ -642,6 +648,9 @@ struct qed_hwfn {
 #endif
 
        struct z_stream_s               *stream;
+       struct workqueue_struct *slowpath_wq;
+       struct delayed_work slowpath_task;
+       unsigned long slowpath_task_flags;
 };
 
 struct pci_params {
@@ -906,5 +915,9 @@ void qed_get_protocol_stats(struct qed_dev *cdev,
                            union qed_mcp_protocol_stats *stats);
 int qed_slowpath_irq_req(struct qed_hwfn *hwfn);
 void qed_slowpath_irq_sync(struct qed_hwfn *p_hwfn);
+int qed_mfw_tlv_req(struct qed_hwfn *hwfn);
 
+int qed_mfw_fill_tlv_data(struct qed_hwfn *hwfn,
+                         enum qed_mfw_tlv_type type,
+                         union qed_mfw_tlv_data *tlv_data);
 #endif /* _QED_H */
index b5f70ef..8e1e6e1 100644 (file)
@@ -11863,6 +11863,8 @@ struct public_global {
        u32 running_bundle_id;
        s32 external_temperature;
        u32 mdump_reason;
+       u32 data_ptr;
+       u32 data_size;
 };
 
 struct fw_flr_mb {
@@ -12322,6 +12324,7 @@ struct public_drv_mb {
 #define DRV_MSG_CODE_BIST_TEST                 0x001e0000
 #define DRV_MSG_CODE_SET_LED_MODE              0x00200000
 #define DRV_MSG_CODE_RESOURCE_CMD      0x00230000
+#define DRV_MSG_CODE_GET_TLV_DONE              0x002f0000
 
 #define RESOURCE_CMD_REQ_RESC_MASK             0x0000001F
 #define RESOURCE_CMD_REQ_RESC_SHIFT            0
@@ -12523,6 +12526,7 @@ enum MFW_DRV_MSG_TYPE {
        MFW_DRV_MSG_TRANSCEIVER_STATE_CHANGE,
        MFW_DRV_MSG_BW_UPDATE11,
        MFW_DRV_MSG_OEM_CFG_UPDATE,
+       MFW_DRV_MSG_GET_TLV_REQ,
        MFW_DRV_MSG_MAX
 };
 
@@ -12558,6 +12562,233 @@ struct mcp_public_data {
        struct public_func func[MCP_GLOB_FUNC_MAX];
 };
 
+/* OCBB definitions */
+enum tlvs {
+       /* Category 1: Device Properties */
+       DRV_TLV_CLP_STR,
+       DRV_TLV_CLP_STR_CTD,
+       /* Category 6: Device Configuration */
+       DRV_TLV_SCSI_TO,
+       DRV_TLV_R_T_TOV,
+       DRV_TLV_R_A_TOV,
+       DRV_TLV_E_D_TOV,
+       DRV_TLV_CR_TOV,
+       DRV_TLV_BOOT_TYPE,
+       /* Category 8: Port Configuration */
+       DRV_TLV_NPIV_ENABLED,
+       /* Category 10: Function Configuration */
+       DRV_TLV_FEATURE_FLAGS,
+       DRV_TLV_LOCAL_ADMIN_ADDR,
+       DRV_TLV_ADDITIONAL_MAC_ADDR_1,
+       DRV_TLV_ADDITIONAL_MAC_ADDR_2,
+       DRV_TLV_LSO_MAX_OFFLOAD_SIZE,
+       DRV_TLV_LSO_MIN_SEGMENT_COUNT,
+       DRV_TLV_PROMISCUOUS_MODE,
+       DRV_TLV_TX_DESCRIPTORS_QUEUE_SIZE,
+       DRV_TLV_RX_DESCRIPTORS_QUEUE_SIZE,
+       DRV_TLV_NUM_OF_NET_QUEUE_VMQ_CFG,
+       DRV_TLV_FLEX_NIC_OUTER_VLAN_ID,
+       DRV_TLV_OS_DRIVER_STATES,
+       DRV_TLV_PXE_BOOT_PROGRESS,
+       /* Category 12: FC/FCoE Configuration */
+       DRV_TLV_NPIV_STATE,
+       DRV_TLV_NUM_OF_NPIV_IDS,
+       DRV_TLV_SWITCH_NAME,
+       DRV_TLV_SWITCH_PORT_NUM,
+       DRV_TLV_SWITCH_PORT_ID,
+       DRV_TLV_VENDOR_NAME,
+       DRV_TLV_SWITCH_MODEL,
+       DRV_TLV_SWITCH_FW_VER,
+       DRV_TLV_QOS_PRIORITY_PER_802_1P,
+       DRV_TLV_PORT_ALIAS,
+       DRV_TLV_PORT_STATE,
+       DRV_TLV_FIP_TX_DESCRIPTORS_QUEUE_SIZE,
+       DRV_TLV_FCOE_RX_DESCRIPTORS_QUEUE_SIZE,
+       DRV_TLV_LINK_FAILURE_COUNT,
+       DRV_TLV_FCOE_BOOT_PROGRESS,
+       /* Category 13: iSCSI Configuration */
+       DRV_TLV_TARGET_LLMNR_ENABLED,
+       DRV_TLV_HEADER_DIGEST_FLAG_ENABLED,
+       DRV_TLV_DATA_DIGEST_FLAG_ENABLED,
+       DRV_TLV_AUTHENTICATION_METHOD,
+       DRV_TLV_ISCSI_BOOT_TARGET_PORTAL,
+       DRV_TLV_MAX_FRAME_SIZE,
+       DRV_TLV_PDU_TX_DESCRIPTORS_QUEUE_SIZE,
+       DRV_TLV_PDU_RX_DESCRIPTORS_QUEUE_SIZE,
+       DRV_TLV_ISCSI_BOOT_PROGRESS,
+       /* Category 20: Device Data */
+       DRV_TLV_PCIE_BUS_RX_UTILIZATION,
+       DRV_TLV_PCIE_BUS_TX_UTILIZATION,
+       DRV_TLV_DEVICE_CPU_CORES_UTILIZATION,
+       DRV_TLV_LAST_VALID_DCC_TLV_RECEIVED,
+       DRV_TLV_NCSI_RX_BYTES_RECEIVED,
+       DRV_TLV_NCSI_TX_BYTES_SENT,
+       /* Category 22: Base Port Data */
+       DRV_TLV_RX_DISCARDS,
+       DRV_TLV_RX_ERRORS,
+       DRV_TLV_TX_ERRORS,
+       DRV_TLV_TX_DISCARDS,
+       DRV_TLV_RX_FRAMES_RECEIVED,
+       DRV_TLV_TX_FRAMES_SENT,
+       /* Category 23: FC/FCoE Port Data */
+       DRV_TLV_RX_BROADCAST_PACKETS,
+       DRV_TLV_TX_BROADCAST_PACKETS,
+       /* Category 28: Base Function Data */
+       DRV_TLV_NUM_OFFLOADED_CONNECTIONS_TCP_IPV4,
+       DRV_TLV_NUM_OFFLOADED_CONNECTIONS_TCP_IPV6,
+       DRV_TLV_TX_DESCRIPTOR_QUEUE_AVG_DEPTH,
+       DRV_TLV_RX_DESCRIPTORS_QUEUE_AVG_DEPTH,
+       DRV_TLV_PF_RX_FRAMES_RECEIVED,
+       DRV_TLV_RX_BYTES_RECEIVED,
+       DRV_TLV_PF_TX_FRAMES_SENT,
+       DRV_TLV_TX_BYTES_SENT,
+       DRV_TLV_IOV_OFFLOAD,
+       DRV_TLV_PCI_ERRORS_CAP_ID,
+       DRV_TLV_UNCORRECTABLE_ERROR_STATUS,
+       DRV_TLV_UNCORRECTABLE_ERROR_MASK,
+       DRV_TLV_CORRECTABLE_ERROR_STATUS,
+       DRV_TLV_CORRECTABLE_ERROR_MASK,
+       DRV_TLV_PCI_ERRORS_AECC_REGISTER,
+       DRV_TLV_TX_QUEUES_EMPTY,
+       DRV_TLV_RX_QUEUES_EMPTY,
+       DRV_TLV_TX_QUEUES_FULL,
+       DRV_TLV_RX_QUEUES_FULL,
+       /* Category 29: FC/FCoE Function Data */
+       DRV_TLV_FCOE_TX_DESCRIPTOR_QUEUE_AVG_DEPTH,
+       DRV_TLV_FCOE_RX_DESCRIPTORS_QUEUE_AVG_DEPTH,
+       DRV_TLV_FCOE_RX_FRAMES_RECEIVED,
+       DRV_TLV_FCOE_RX_BYTES_RECEIVED,
+       DRV_TLV_FCOE_TX_FRAMES_SENT,
+       DRV_TLV_FCOE_TX_BYTES_SENT,
+       DRV_TLV_CRC_ERROR_COUNT,
+       DRV_TLV_CRC_ERROR_1_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_CRC_ERROR_1_TIMESTAMP,
+       DRV_TLV_CRC_ERROR_2_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_CRC_ERROR_2_TIMESTAMP,
+       DRV_TLV_CRC_ERROR_3_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_CRC_ERROR_3_TIMESTAMP,
+       DRV_TLV_CRC_ERROR_4_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_CRC_ERROR_4_TIMESTAMP,
+       DRV_TLV_CRC_ERROR_5_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_CRC_ERROR_5_TIMESTAMP,
+       DRV_TLV_LOSS_OF_SYNC_ERROR_COUNT,
+       DRV_TLV_LOSS_OF_SIGNAL_ERRORS,
+       DRV_TLV_PRIMITIVE_SEQUENCE_PROTOCOL_ERROR_COUNT,
+       DRV_TLV_DISPARITY_ERROR_COUNT,
+       DRV_TLV_CODE_VIOLATION_ERROR_COUNT,
+       DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_1,
+       DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_2,
+       DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_3,
+       DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_4,
+       DRV_TLV_LAST_FLOGI_TIMESTAMP,
+       DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_1,
+       DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_2,
+       DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_3,
+       DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_4,
+       DRV_TLV_LAST_FLOGI_ACC_TIMESTAMP,
+       DRV_TLV_LAST_FLOGI_RJT,
+       DRV_TLV_LAST_FLOGI_RJT_TIMESTAMP,
+       DRV_TLV_FDISCS_SENT_COUNT,
+       DRV_TLV_FDISC_ACCS_RECEIVED,
+       DRV_TLV_FDISC_RJTS_RECEIVED,
+       DRV_TLV_PLOGI_SENT_COUNT,
+       DRV_TLV_PLOGI_ACCS_RECEIVED,
+       DRV_TLV_PLOGI_RJTS_RECEIVED,
+       DRV_TLV_PLOGI_1_SENT_DESTINATION_FC_ID,
+       DRV_TLV_PLOGI_1_TIMESTAMP,
+       DRV_TLV_PLOGI_2_SENT_DESTINATION_FC_ID,
+       DRV_TLV_PLOGI_2_TIMESTAMP,
+       DRV_TLV_PLOGI_3_SENT_DESTINATION_FC_ID,
+       DRV_TLV_PLOGI_3_TIMESTAMP,
+       DRV_TLV_PLOGI_4_SENT_DESTINATION_FC_ID,
+       DRV_TLV_PLOGI_4_TIMESTAMP,
+       DRV_TLV_PLOGI_5_SENT_DESTINATION_FC_ID,
+       DRV_TLV_PLOGI_5_TIMESTAMP,
+       DRV_TLV_PLOGI_1_ACC_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_PLOGI_1_ACC_TIMESTAMP,
+       DRV_TLV_PLOGI_2_ACC_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_PLOGI_2_ACC_TIMESTAMP,
+       DRV_TLV_PLOGI_3_ACC_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_PLOGI_3_ACC_TIMESTAMP,
+       DRV_TLV_PLOGI_4_ACC_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_PLOGI_4_ACC_TIMESTAMP,
+       DRV_TLV_PLOGI_5_ACC_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_PLOGI_5_ACC_TIMESTAMP,
+       DRV_TLV_LOGOS_ISSUED,
+       DRV_TLV_LOGO_ACCS_RECEIVED,
+       DRV_TLV_LOGO_RJTS_RECEIVED,
+       DRV_TLV_LOGO_1_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_LOGO_1_TIMESTAMP,
+       DRV_TLV_LOGO_2_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_LOGO_2_TIMESTAMP,
+       DRV_TLV_LOGO_3_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_LOGO_3_TIMESTAMP,
+       DRV_TLV_LOGO_4_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_LOGO_4_TIMESTAMP,
+       DRV_TLV_LOGO_5_RECEIVED_SOURCE_FC_ID,
+       DRV_TLV_LOGO_5_TIMESTAMP,
+       DRV_TLV_LOGOS_RECEIVED,
+       DRV_TLV_ACCS_ISSUED,
+       DRV_TLV_PRLIS_ISSUED,
+       DRV_TLV_ACCS_RECEIVED,
+       DRV_TLV_ABTS_SENT_COUNT,
+       DRV_TLV_ABTS_ACCS_RECEIVED,
+       DRV_TLV_ABTS_RJTS_RECEIVED,
+       DRV_TLV_ABTS_1_SENT_DESTINATION_FC_ID,
+       DRV_TLV_ABTS_1_TIMESTAMP,
+       DRV_TLV_ABTS_2_SENT_DESTINATION_FC_ID,
+       DRV_TLV_ABTS_2_TIMESTAMP,
+       DRV_TLV_ABTS_3_SENT_DESTINATION_FC_ID,
+       DRV_TLV_ABTS_3_TIMESTAMP,
+       DRV_TLV_ABTS_4_SENT_DESTINATION_FC_ID,
+       DRV_TLV_ABTS_4_TIMESTAMP,
+       DRV_TLV_ABTS_5_SENT_DESTINATION_FC_ID,
+       DRV_TLV_ABTS_5_TIMESTAMP,
+       DRV_TLV_RSCNS_RECEIVED,
+       DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_1,
+       DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_2,
+       DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_3,
+       DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_4,
+       DRV_TLV_LUN_RESETS_ISSUED,
+       DRV_TLV_ABORT_TASK_SETS_ISSUED,
+       DRV_TLV_TPRLOS_SENT,
+       DRV_TLV_NOS_SENT_COUNT,
+       DRV_TLV_NOS_RECEIVED_COUNT,
+       DRV_TLV_OLS_COUNT,
+       DRV_TLV_LR_COUNT,
+       DRV_TLV_LRR_COUNT,
+       DRV_TLV_LIP_SENT_COUNT,
+       DRV_TLV_LIP_RECEIVED_COUNT,
+       DRV_TLV_EOFA_COUNT,
+       DRV_TLV_EOFNI_COUNT,
+       DRV_TLV_SCSI_STATUS_CHECK_CONDITION_COUNT,
+       DRV_TLV_SCSI_STATUS_CONDITION_MET_COUNT,
+       DRV_TLV_SCSI_STATUS_BUSY_COUNT,
+       DRV_TLV_SCSI_STATUS_INTERMEDIATE_COUNT,
+       DRV_TLV_SCSI_STATUS_INTERMEDIATE_CONDITION_MET_COUNT,
+       DRV_TLV_SCSI_STATUS_RESERVATION_CONFLICT_COUNT,
+       DRV_TLV_SCSI_STATUS_TASK_SET_FULL_COUNT,
+       DRV_TLV_SCSI_STATUS_ACA_ACTIVE_COUNT,
+       DRV_TLV_SCSI_STATUS_TASK_ABORTED_COUNT,
+       DRV_TLV_SCSI_CHECK_CONDITION_1_RECEIVED_SK_ASC_ASCQ,
+       DRV_TLV_SCSI_CHECK_1_TIMESTAMP,
+       DRV_TLV_SCSI_CHECK_CONDITION_2_RECEIVED_SK_ASC_ASCQ,
+       DRV_TLV_SCSI_CHECK_2_TIMESTAMP,
+       DRV_TLV_SCSI_CHECK_CONDITION_3_RECEIVED_SK_ASC_ASCQ,
+       DRV_TLV_SCSI_CHECK_3_TIMESTAMP,
+       DRV_TLV_SCSI_CHECK_CONDITION_4_RECEIVED_SK_ASC_ASCQ,
+       DRV_TLV_SCSI_CHECK_4_TIMESTAMP,
+       DRV_TLV_SCSI_CHECK_CONDITION_5_RECEIVED_SK_ASC_ASCQ,
+       DRV_TLV_SCSI_CHECK_5_TIMESTAMP,
+       /* Category 30: iSCSI Function Data */
+       DRV_TLV_PDU_TX_DESCRIPTOR_QUEUE_AVG_DEPTH,
+       DRV_TLV_PDU_RX_DESCRIPTORS_QUEUE_AVG_DEPTH,
+       DRV_TLV_ISCSI_PDU_RX_FRAMES_RECEIVED,
+       DRV_TLV_ISCSI_PDU_RX_BYTES_RECEIVED,
+       DRV_TLV_ISCSI_PDU_TX_FRAMES_SENT,
+       DRV_TLV_ISCSI_PDU_TX_BYTES_SENT
+};
+
 struct nvm_cfg_mac_address {
        u32 mac_addr_hi;
 #define NVM_CFG_MAC_ADDRESS_HI_MASK    0x0000FFFF
index c3c1a99..c97ebd6 100644 (file)
@@ -292,6 +292,7 @@ static void qed_ll2_txq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
        struct qed_ll2_tx_packet *p_pkt = NULL;
        struct qed_ll2_info *p_ll2_conn;
        struct qed_ll2_tx_queue *p_tx;
+       unsigned long flags = 0;
        dma_addr_t tx_frag;
 
        p_ll2_conn = qed_ll2_handle_sanity_inactive(p_hwfn, connection_handle);
@@ -300,6 +301,7 @@ static void qed_ll2_txq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
 
        p_tx = &p_ll2_conn->tx_queue;
 
+       spin_lock_irqsave(&p_tx->lock, flags);
        while (!list_empty(&p_tx->active_descq)) {
                p_pkt = list_first_entry(&p_tx->active_descq,
                                         struct qed_ll2_tx_packet, list_entry);
@@ -309,6 +311,7 @@ static void qed_ll2_txq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
                list_del(&p_pkt->list_entry);
                b_last_packet = list_empty(&p_tx->active_descq);
                list_add_tail(&p_pkt->list_entry, &p_tx->free_descq);
+               spin_unlock_irqrestore(&p_tx->lock, flags);
                if (p_ll2_conn->input.conn_type == QED_LL2_TYPE_OOO) {
                        struct qed_ooo_buffer *p_buffer;
 
@@ -328,7 +331,9 @@ static void qed_ll2_txq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
                                                      b_last_frag,
                                                      b_last_packet);
                }
+               spin_lock_irqsave(&p_tx->lock, flags);
        }
+       spin_unlock_irqrestore(&p_tx->lock, flags);
 }
 
 static int qed_ll2_txq_completion(struct qed_hwfn *p_hwfn, void *p_cookie)
@@ -556,6 +561,7 @@ static void qed_ll2_rxq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
        struct qed_ll2_info *p_ll2_conn = NULL;
        struct qed_ll2_rx_packet *p_pkt = NULL;
        struct qed_ll2_rx_queue *p_rx;
+       unsigned long flags = 0;
 
        p_ll2_conn = qed_ll2_handle_sanity_inactive(p_hwfn, connection_handle);
        if (!p_ll2_conn)
@@ -563,13 +569,14 @@ static void qed_ll2_rxq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
 
        p_rx = &p_ll2_conn->rx_queue;
 
+       spin_lock_irqsave(&p_rx->lock, flags);
        while (!list_empty(&p_rx->active_descq)) {
                p_pkt = list_first_entry(&p_rx->active_descq,
                                         struct qed_ll2_rx_packet, list_entry);
                if (!p_pkt)
                        break;
-
                list_move_tail(&p_pkt->list_entry, &p_rx->free_descq);
+               spin_unlock_irqrestore(&p_rx->lock, flags);
 
                if (p_ll2_conn->input.conn_type == QED_LL2_TYPE_OOO) {
                        struct qed_ooo_buffer *p_buffer;
@@ -588,7 +595,30 @@ static void qed_ll2_rxq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
                                                      cookie,
                                                      rx_buf_addr, b_last);
                }
+               spin_lock_irqsave(&p_rx->lock, flags);
        }
+       spin_unlock_irqrestore(&p_rx->lock, flags);
+}
+
+static bool
+qed_ll2_lb_rxq_handler_slowpath(struct qed_hwfn *p_hwfn,
+                               struct core_rx_slow_path_cqe *p_cqe)
+{
+       struct ooo_opaque *iscsi_ooo;
+       u32 cid;
+
+       if (p_cqe->ramrod_cmd_id != CORE_RAMROD_RX_QUEUE_FLUSH)
+               return false;
+
+       iscsi_ooo = (struct ooo_opaque *)&p_cqe->opaque_data;
+       if (iscsi_ooo->ooo_opcode != TCP_EVENT_DELETE_ISLES)
+               return false;
+
+       /* Need to make a flush */
+       cid = le32_to_cpu(iscsi_ooo->cid);
+       qed_ooo_release_connection_isles(p_hwfn, p_hwfn->p_ooo_info, cid);
+
+       return true;
 }
 
 static int qed_ll2_lb_rxq_handler(struct qed_hwfn *p_hwfn,
@@ -617,6 +647,11 @@ static int qed_ll2_lb_rxq_handler(struct qed_hwfn *p_hwfn,
                cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain);
                cqe_type = cqe->rx_cqe_sp.type;
 
+               if (cqe_type == CORE_RX_CQE_TYPE_SLOW_PATH)
+                       if (qed_ll2_lb_rxq_handler_slowpath(p_hwfn,
+                                                           &cqe->rx_cqe_sp))
+                               continue;
+
                if (cqe_type != CORE_RX_CQE_TYPE_REGULAR) {
                        DP_NOTICE(p_hwfn,
                                  "Got a non-regular LB LL2 completion [type 0x%02x]\n",
@@ -794,6 +829,9 @@ static int qed_ll2_lb_rxq_completion(struct qed_hwfn *p_hwfn, void *p_cookie)
        struct qed_ll2_info *p_ll2_conn = (struct qed_ll2_info *)p_cookie;
        int rc;
 
+       if (!QED_LL2_RX_REGISTERED(p_ll2_conn))
+               return 0;
+
        rc = qed_ll2_lb_rxq_handler(p_hwfn, p_ll2_conn);
        if (rc)
                return rc;
@@ -814,6 +852,9 @@ static int qed_ll2_lb_txq_completion(struct qed_hwfn *p_hwfn, void *p_cookie)
        u16 new_idx = 0, num_bds = 0;
        int rc;
 
+       if (!QED_LL2_TX_REGISTERED(p_ll2_conn))
+               return 0;
+
        new_idx = le16_to_cpu(*p_tx->p_fw_cons);
        num_bds = ((s16)new_idx - (s16)p_tx->bds_idx);
 
@@ -1880,17 +1921,25 @@ int qed_ll2_terminate_connection(void *cxt, u8 connection_handle)
 
        /* Stop Tx & Rx of connection, if needed */
        if (QED_LL2_TX_REGISTERED(p_ll2_conn)) {
+               p_ll2_conn->tx_queue.b_cb_registred = false;
+               smp_wmb(); /* Make sure this is seen by ll2_lb_rxq_completion */
                rc = qed_sp_ll2_tx_queue_stop(p_hwfn, p_ll2_conn);
                if (rc)
                        goto out;
+
                qed_ll2_txq_flush(p_hwfn, connection_handle);
+               qed_int_unregister_cb(p_hwfn, p_ll2_conn->tx_queue.tx_sb_index);
        }
 
        if (QED_LL2_RX_REGISTERED(p_ll2_conn)) {
+               p_ll2_conn->rx_queue.b_cb_registred = false;
+               smp_wmb(); /* Make sure this is seen by ll2_lb_rxq_completion */
                rc = qed_sp_ll2_rx_queue_stop(p_hwfn, p_ll2_conn);
                if (rc)
                        goto out;
+
                qed_ll2_rxq_flush(p_hwfn, connection_handle);
+               qed_int_unregister_cb(p_hwfn, p_ll2_conn->rx_queue.rx_sb_index);
        }
 
        if (p_ll2_conn->input.conn_type == QED_LL2_TYPE_OOO)
@@ -1939,16 +1988,6 @@ void qed_ll2_release_connection(void *cxt, u8 connection_handle)
        if (!p_ll2_conn)
                return;
 
-       if (QED_LL2_RX_REGISTERED(p_ll2_conn)) {
-               p_ll2_conn->rx_queue.b_cb_registred = false;
-               qed_int_unregister_cb(p_hwfn, p_ll2_conn->rx_queue.rx_sb_index);
-       }
-
-       if (QED_LL2_TX_REGISTERED(p_ll2_conn)) {
-               p_ll2_conn->tx_queue.b_cb_registred = false;
-               qed_int_unregister_cb(p_hwfn, p_ll2_conn->tx_queue.tx_sb_index);
-       }
-
        kfree(p_ll2_conn->tx_queue.descq_mem);
        qed_chain_free(p_hwfn->cdev, &p_ll2_conn->tx_queue.txq_chain);
 
index 9feed3b..68c4399 100644 (file)
@@ -946,6 +946,68 @@ static void qed_update_pf_params(struct qed_dev *cdev,
        }
 }
 
+static void qed_slowpath_wq_stop(struct qed_dev *cdev)
+{
+       int i;
+
+       if (IS_VF(cdev))
+               return;
+
+       for_each_hwfn(cdev, i) {
+               if (!cdev->hwfns[i].slowpath_wq)
+                       continue;
+
+               flush_workqueue(cdev->hwfns[i].slowpath_wq);
+               destroy_workqueue(cdev->hwfns[i].slowpath_wq);
+       }
+}
+
+static void qed_slowpath_task(struct work_struct *work)
+{
+       struct qed_hwfn *hwfn = container_of(work, struct qed_hwfn,
+                                            slowpath_task.work);
+       struct qed_ptt *ptt = qed_ptt_acquire(hwfn);
+
+       if (!ptt) {
+               queue_delayed_work(hwfn->slowpath_wq, &hwfn->slowpath_task, 0);
+               return;
+       }
+
+       if (test_and_clear_bit(QED_SLOWPATH_MFW_TLV_REQ,
+                              &hwfn->slowpath_task_flags))
+               qed_mfw_process_tlv_req(hwfn, ptt);
+
+       qed_ptt_release(hwfn, ptt);
+}
+
+static int qed_slowpath_wq_start(struct qed_dev *cdev)
+{
+       struct qed_hwfn *hwfn;
+       char name[NAME_SIZE];
+       int i;
+
+       if (IS_VF(cdev))
+               return 0;
+
+       for_each_hwfn(cdev, i) {
+               hwfn = &cdev->hwfns[i];
+
+               snprintf(name, NAME_SIZE, "slowpath-%02x:%02x.%02x",
+                        cdev->pdev->bus->number,
+                        PCI_SLOT(cdev->pdev->devfn), hwfn->abs_pf_id);
+
+               hwfn->slowpath_wq = alloc_workqueue(name, 0, 0);
+               if (!hwfn->slowpath_wq) {
+                       DP_NOTICE(hwfn, "Cannot create slowpath workqueue\n");
+                       return -ENOMEM;
+               }
+
+               INIT_DELAYED_WORK(&hwfn->slowpath_task, qed_slowpath_task);
+       }
+
+       return 0;
+}
+
 static int qed_slowpath_start(struct qed_dev *cdev,
                              struct qed_slowpath_params *params)
 {
@@ -961,6 +1023,9 @@ static int qed_slowpath_start(struct qed_dev *cdev,
        if (qed_iov_wq_start(cdev))
                goto err;
 
+       if (qed_slowpath_wq_start(cdev))
+               goto err;
+
        if (IS_PF(cdev)) {
                rc = request_firmware(&cdev->firmware, QED_FW_FILE_NAME,
                                      &cdev->pdev->dev);
@@ -1095,6 +1160,8 @@ err:
 
        qed_iov_wq_stop(cdev, false);
 
+       qed_slowpath_wq_stop(cdev);
+
        return rc;
 }
 
@@ -1103,6 +1170,8 @@ static int qed_slowpath_stop(struct qed_dev *cdev)
        if (!cdev)
                return -ENODEV;
 
+       qed_slowpath_wq_stop(cdev);
+
        qed_ll2_dealloc_if(cdev);
 
        if (IS_PF(cdev)) {
@@ -2088,3 +2157,89 @@ void qed_get_protocol_stats(struct qed_dev *cdev,
                return;
        }
 }
+
+int qed_mfw_tlv_req(struct qed_hwfn *hwfn)
+{
+       DP_VERBOSE(hwfn->cdev, NETIF_MSG_DRV,
+                  "Scheduling slowpath task [Flag: %d]\n",
+                  QED_SLOWPATH_MFW_TLV_REQ);
+       smp_mb__before_atomic();
+       set_bit(QED_SLOWPATH_MFW_TLV_REQ, &hwfn->slowpath_task_flags);
+       smp_mb__after_atomic();
+       queue_delayed_work(hwfn->slowpath_wq, &hwfn->slowpath_task, 0);
+
+       return 0;
+}
+
+static void
+qed_fill_generic_tlv_data(struct qed_dev *cdev, struct qed_mfw_tlv_generic *tlv)
+{
+       struct qed_common_cb_ops *op = cdev->protocol_ops.common;
+       struct qed_eth_stats_common *p_common;
+       struct qed_generic_tlvs gen_tlvs;
+       struct qed_eth_stats stats;
+       int i;
+
+       memset(&gen_tlvs, 0, sizeof(gen_tlvs));
+       op->get_generic_tlv_data(cdev->ops_cookie, &gen_tlvs);
+
+       if (gen_tlvs.feat_flags & QED_TLV_IP_CSUM)
+               tlv->flags.ipv4_csum_offload = true;
+       if (gen_tlvs.feat_flags & QED_TLV_LSO)
+               tlv->flags.lso_supported = true;
+       tlv->flags.b_set = true;
+
+       for (i = 0; i < QED_TLV_MAC_COUNT; i++) {
+               if (is_valid_ether_addr(gen_tlvs.mac[i])) {
+                       ether_addr_copy(tlv->mac[i], gen_tlvs.mac[i]);
+                       tlv->mac_set[i] = true;
+               }
+       }
+
+       qed_get_vport_stats(cdev, &stats);
+       p_common = &stats.common;
+       tlv->rx_frames = p_common->rx_ucast_pkts + p_common->rx_mcast_pkts +
+                        p_common->rx_bcast_pkts;
+       tlv->rx_frames_set = true;
+       tlv->rx_bytes = p_common->rx_ucast_bytes + p_common->rx_mcast_bytes +
+                       p_common->rx_bcast_bytes;
+       tlv->rx_bytes_set = true;
+       tlv->tx_frames = p_common->tx_ucast_pkts + p_common->tx_mcast_pkts +
+                        p_common->tx_bcast_pkts;
+       tlv->tx_frames_set = true;
+       tlv->tx_bytes = p_common->tx_ucast_bytes + p_common->tx_mcast_bytes +
+                       p_common->tx_bcast_bytes;
+       tlv->rx_bytes_set = true;
+}
+
+int qed_mfw_fill_tlv_data(struct qed_hwfn *hwfn, enum qed_mfw_tlv_type type,
+                         union qed_mfw_tlv_data *tlv_buf)
+{
+       struct qed_dev *cdev = hwfn->cdev;
+       struct qed_common_cb_ops *ops;
+
+       ops = cdev->protocol_ops.common;
+       if (!ops || !ops->get_protocol_tlv_data || !ops->get_generic_tlv_data) {
+               DP_NOTICE(hwfn, "Can't collect TLV management info\n");
+               return -EINVAL;
+       }
+
+       switch (type) {
+       case QED_MFW_TLV_GENERIC:
+               qed_fill_generic_tlv_data(hwfn->cdev, &tlv_buf->generic);
+               break;
+       case QED_MFW_TLV_ETH:
+               ops->get_protocol_tlv_data(cdev->ops_cookie, &tlv_buf->eth);
+               break;
+       case QED_MFW_TLV_FCOE:
+               ops->get_protocol_tlv_data(cdev->ops_cookie, &tlv_buf->fcoe);
+               break;
+       case QED_MFW_TLV_ISCSI:
+               ops->get_protocol_tlv_data(cdev->ops_cookie, &tlv_buf->iscsi);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
index e80f5e7..2612e3e 100644 (file)
@@ -1622,6 +1622,8 @@ int qed_mcp_handle_events(struct qed_hwfn *p_hwfn,
                case MFW_DRV_MSG_S_TAG_UPDATE:
                        qed_mcp_update_stag(p_hwfn, p_ptt);
                        break;
+               case MFW_DRV_MSG_GET_TLV_REQ:
+                       qed_mfw_tlv_req(p_hwfn);
                        break;
                default:
                        DP_INFO(p_hwfn, "Unimplemented MFW message %d\n", i);
index 250579b..632a838 100644 (file)
@@ -213,6 +213,44 @@ enum qed_ov_wol {
        QED_OV_WOL_ENABLED
 };
 
+enum qed_mfw_tlv_type {
+       QED_MFW_TLV_GENERIC = 0x1,      /* Core driver TLVs */
+       QED_MFW_TLV_ETH = 0x2,          /* L2 driver TLVs */
+       QED_MFW_TLV_FCOE = 0x4,         /* FCoE protocol TLVs */
+       QED_MFW_TLV_ISCSI = 0x8,        /* SCSI protocol TLVs */
+       QED_MFW_TLV_MAX = 0x16,
+};
+
+struct qed_mfw_tlv_generic {
+#define QED_MFW_TLV_FLAGS_SIZE 2
+       struct {
+               u8 ipv4_csum_offload;
+               u8 lso_supported;
+               bool b_set;
+       } flags;
+
+#define QED_MFW_TLV_MAC_COUNT 3
+       /* First entry for primary MAC, 2 secondary MACs possible */
+       u8 mac[QED_MFW_TLV_MAC_COUNT][6];
+       bool mac_set[QED_MFW_TLV_MAC_COUNT];
+
+       u64 rx_frames;
+       bool rx_frames_set;
+       u64 rx_bytes;
+       bool rx_bytes_set;
+       u64 tx_frames;
+       bool tx_frames_set;
+       u64 tx_bytes;
+       bool tx_bytes_set;
+};
+
+union qed_mfw_tlv_data {
+       struct qed_mfw_tlv_generic generic;
+       struct qed_mfw_tlv_eth eth;
+       struct qed_mfw_tlv_fcoe fcoe;
+       struct qed_mfw_tlv_iscsi iscsi;
+};
+
 /**
  * @brief - returns the link params of the hw function
  *
@@ -561,6 +599,17 @@ int qed_mcp_bist_nvm_get_image_att(struct qed_hwfn *p_hwfn,
                                   struct bist_nvm_image_att *p_image_att,
                                   u32 image_index);
 
+/**
+ * @brief - Processes the TLV request from MFW i.e., get the required TLV info
+ *          from the qed client and send it to the MFW.
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ *
+ * @param return 0 upon success.
+ */
+int qed_mfw_process_tlv_req(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
+
 /* Using hwfn number (and not pf_num) is required since in CMT mode,
  * same pf_num may be used by two different hwfn
  * TODO - this shouldn't really be in .h file, but until all fields
@@ -621,6 +670,14 @@ struct qed_mcp_mb_params {
        u32                     mcp_param;
 };
 
+struct qed_drv_tlv_hdr {
+       u8 tlv_type;
+       u8 tlv_length;  /* In dwords - not including this header */
+       u8 tlv_reserved;
+#define QED_DRV_TLV_FLAGS_CHANGED 0x01
+       u8 tlv_flags;
+};
+
 /**
  * @brief Initialize the interface with the MCP
  *
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mng_tlv.c b/drivers/net/ethernet/qlogic/qed/qed_mng_tlv.c
new file mode 100644 (file)
index 0000000..6c16158
--- /dev/null
@@ -0,0 +1,1337 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+#include "qed.h"
+#include "qed_hw.h"
+#include "qed_mcp.h"
+#include "qed_reg_addr.h"
+
+#define TLV_TYPE(p)     (p[0])
+#define TLV_LENGTH(p)   (p[1])
+#define TLV_FLAGS(p)    (p[3])
+
+#define QED_TLV_DATA_MAX (14)
+struct qed_tlv_parsed_buf {
+       /* To be filled with the address to set in Value field */
+       void *p_val;
+
+       /* To be used internally in case the value has to be modified */
+       u8 data[QED_TLV_DATA_MAX];
+};
+
+static int qed_mfw_get_tlv_group(u8 tlv_type, u8 *tlv_group)
+{
+       switch (tlv_type) {
+       case DRV_TLV_FEATURE_FLAGS:
+       case DRV_TLV_LOCAL_ADMIN_ADDR:
+       case DRV_TLV_ADDITIONAL_MAC_ADDR_1:
+       case DRV_TLV_ADDITIONAL_MAC_ADDR_2:
+       case DRV_TLV_OS_DRIVER_STATES:
+       case DRV_TLV_PXE_BOOT_PROGRESS:
+       case DRV_TLV_RX_FRAMES_RECEIVED:
+       case DRV_TLV_RX_BYTES_RECEIVED:
+       case DRV_TLV_TX_FRAMES_SENT:
+       case DRV_TLV_TX_BYTES_SENT:
+       case DRV_TLV_NPIV_ENABLED:
+       case DRV_TLV_PCIE_BUS_RX_UTILIZATION:
+       case DRV_TLV_PCIE_BUS_TX_UTILIZATION:
+       case DRV_TLV_DEVICE_CPU_CORES_UTILIZATION:
+       case DRV_TLV_LAST_VALID_DCC_TLV_RECEIVED:
+       case DRV_TLV_NCSI_RX_BYTES_RECEIVED:
+       case DRV_TLV_NCSI_TX_BYTES_SENT:
+               *tlv_group |= QED_MFW_TLV_GENERIC;
+               break;
+       case DRV_TLV_LSO_MAX_OFFLOAD_SIZE:
+       case DRV_TLV_LSO_MIN_SEGMENT_COUNT:
+       case DRV_TLV_PROMISCUOUS_MODE:
+       case DRV_TLV_TX_DESCRIPTORS_QUEUE_SIZE:
+       case DRV_TLV_RX_DESCRIPTORS_QUEUE_SIZE:
+       case DRV_TLV_NUM_OF_NET_QUEUE_VMQ_CFG:
+       case DRV_TLV_NUM_OFFLOADED_CONNECTIONS_TCP_IPV4:
+       case DRV_TLV_NUM_OFFLOADED_CONNECTIONS_TCP_IPV6:
+       case DRV_TLV_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
+       case DRV_TLV_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
+       case DRV_TLV_IOV_OFFLOAD:
+       case DRV_TLV_TX_QUEUES_EMPTY:
+       case DRV_TLV_RX_QUEUES_EMPTY:
+       case DRV_TLV_TX_QUEUES_FULL:
+       case DRV_TLV_RX_QUEUES_FULL:
+               *tlv_group |= QED_MFW_TLV_ETH;
+               break;
+       case DRV_TLV_SCSI_TO:
+       case DRV_TLV_R_T_TOV:
+       case DRV_TLV_R_A_TOV:
+       case DRV_TLV_E_D_TOV:
+       case DRV_TLV_CR_TOV:
+       case DRV_TLV_BOOT_TYPE:
+       case DRV_TLV_NPIV_STATE:
+       case DRV_TLV_NUM_OF_NPIV_IDS:
+       case DRV_TLV_SWITCH_NAME:
+       case DRV_TLV_SWITCH_PORT_NUM:
+       case DRV_TLV_SWITCH_PORT_ID:
+       case DRV_TLV_VENDOR_NAME:
+       case DRV_TLV_SWITCH_MODEL:
+       case DRV_TLV_SWITCH_FW_VER:
+       case DRV_TLV_QOS_PRIORITY_PER_802_1P:
+       case DRV_TLV_PORT_ALIAS:
+       case DRV_TLV_PORT_STATE:
+       case DRV_TLV_FIP_TX_DESCRIPTORS_QUEUE_SIZE:
+       case DRV_TLV_FCOE_RX_DESCRIPTORS_QUEUE_SIZE:
+       case DRV_TLV_LINK_FAILURE_COUNT:
+       case DRV_TLV_FCOE_BOOT_PROGRESS:
+       case DRV_TLV_RX_BROADCAST_PACKETS:
+       case DRV_TLV_TX_BROADCAST_PACKETS:
+       case DRV_TLV_FCOE_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
+       case DRV_TLV_FCOE_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
+       case DRV_TLV_FCOE_RX_FRAMES_RECEIVED:
+       case DRV_TLV_FCOE_RX_BYTES_RECEIVED:
+       case DRV_TLV_FCOE_TX_FRAMES_SENT:
+       case DRV_TLV_FCOE_TX_BYTES_SENT:
+       case DRV_TLV_CRC_ERROR_COUNT:
+       case DRV_TLV_CRC_ERROR_1_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_CRC_ERROR_1_TIMESTAMP:
+       case DRV_TLV_CRC_ERROR_2_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_CRC_ERROR_2_TIMESTAMP:
+       case DRV_TLV_CRC_ERROR_3_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_CRC_ERROR_3_TIMESTAMP:
+       case DRV_TLV_CRC_ERROR_4_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_CRC_ERROR_4_TIMESTAMP:
+       case DRV_TLV_CRC_ERROR_5_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_CRC_ERROR_5_TIMESTAMP:
+       case DRV_TLV_LOSS_OF_SYNC_ERROR_COUNT:
+       case DRV_TLV_LOSS_OF_SIGNAL_ERRORS:
+       case DRV_TLV_PRIMITIVE_SEQUENCE_PROTOCOL_ERROR_COUNT:
+       case DRV_TLV_DISPARITY_ERROR_COUNT:
+       case DRV_TLV_CODE_VIOLATION_ERROR_COUNT:
+       case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_1:
+       case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_2:
+       case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_3:
+       case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_4:
+       case DRV_TLV_LAST_FLOGI_TIMESTAMP:
+       case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_1:
+       case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_2:
+       case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_3:
+       case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_4:
+       case DRV_TLV_LAST_FLOGI_ACC_TIMESTAMP:
+       case DRV_TLV_LAST_FLOGI_RJT:
+       case DRV_TLV_LAST_FLOGI_RJT_TIMESTAMP:
+       case DRV_TLV_FDISCS_SENT_COUNT:
+       case DRV_TLV_FDISC_ACCS_RECEIVED:
+       case DRV_TLV_FDISC_RJTS_RECEIVED:
+       case DRV_TLV_PLOGI_SENT_COUNT:
+       case DRV_TLV_PLOGI_ACCS_RECEIVED:
+       case DRV_TLV_PLOGI_RJTS_RECEIVED:
+       case DRV_TLV_PLOGI_1_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_PLOGI_1_TIMESTAMP:
+       case DRV_TLV_PLOGI_2_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_PLOGI_2_TIMESTAMP:
+       case DRV_TLV_PLOGI_3_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_PLOGI_3_TIMESTAMP:
+       case DRV_TLV_PLOGI_4_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_PLOGI_4_TIMESTAMP:
+       case DRV_TLV_PLOGI_5_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_PLOGI_5_TIMESTAMP:
+       case DRV_TLV_PLOGI_1_ACC_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_PLOGI_1_ACC_TIMESTAMP:
+       case DRV_TLV_PLOGI_2_ACC_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_PLOGI_2_ACC_TIMESTAMP:
+       case DRV_TLV_PLOGI_3_ACC_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_PLOGI_3_ACC_TIMESTAMP:
+       case DRV_TLV_PLOGI_4_ACC_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_PLOGI_4_ACC_TIMESTAMP:
+       case DRV_TLV_PLOGI_5_ACC_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_PLOGI_5_ACC_TIMESTAMP:
+       case DRV_TLV_LOGOS_ISSUED:
+       case DRV_TLV_LOGO_ACCS_RECEIVED:
+       case DRV_TLV_LOGO_RJTS_RECEIVED:
+       case DRV_TLV_LOGO_1_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_LOGO_1_TIMESTAMP:
+       case DRV_TLV_LOGO_2_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_LOGO_2_TIMESTAMP:
+       case DRV_TLV_LOGO_3_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_LOGO_3_TIMESTAMP:
+       case DRV_TLV_LOGO_4_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_LOGO_4_TIMESTAMP:
+       case DRV_TLV_LOGO_5_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_LOGO_5_TIMESTAMP:
+       case DRV_TLV_LOGOS_RECEIVED:
+       case DRV_TLV_ACCS_ISSUED:
+       case DRV_TLV_PRLIS_ISSUED:
+       case DRV_TLV_ACCS_RECEIVED:
+       case DRV_TLV_ABTS_SENT_COUNT:
+       case DRV_TLV_ABTS_ACCS_RECEIVED:
+       case DRV_TLV_ABTS_RJTS_RECEIVED:
+       case DRV_TLV_ABTS_1_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_ABTS_1_TIMESTAMP:
+       case DRV_TLV_ABTS_2_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_ABTS_2_TIMESTAMP:
+       case DRV_TLV_ABTS_3_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_ABTS_3_TIMESTAMP:
+       case DRV_TLV_ABTS_4_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_ABTS_4_TIMESTAMP:
+       case DRV_TLV_ABTS_5_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_ABTS_5_TIMESTAMP:
+       case DRV_TLV_RSCNS_RECEIVED:
+       case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_1:
+       case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_2:
+       case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_3:
+       case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_4:
+       case DRV_TLV_LUN_RESETS_ISSUED:
+       case DRV_TLV_ABORT_TASK_SETS_ISSUED:
+       case DRV_TLV_TPRLOS_SENT:
+       case DRV_TLV_NOS_SENT_COUNT:
+       case DRV_TLV_NOS_RECEIVED_COUNT:
+       case DRV_TLV_OLS_COUNT:
+       case DRV_TLV_LR_COUNT:
+       case DRV_TLV_LRR_COUNT:
+       case DRV_TLV_LIP_SENT_COUNT:
+       case DRV_TLV_LIP_RECEIVED_COUNT:
+       case DRV_TLV_EOFA_COUNT:
+       case DRV_TLV_EOFNI_COUNT:
+       case DRV_TLV_SCSI_STATUS_CHECK_CONDITION_COUNT:
+       case DRV_TLV_SCSI_STATUS_CONDITION_MET_COUNT:
+       case DRV_TLV_SCSI_STATUS_BUSY_COUNT:
+       case DRV_TLV_SCSI_STATUS_INTERMEDIATE_COUNT:
+       case DRV_TLV_SCSI_STATUS_INTERMEDIATE_CONDITION_MET_COUNT:
+       case DRV_TLV_SCSI_STATUS_RESERVATION_CONFLICT_COUNT:
+       case DRV_TLV_SCSI_STATUS_TASK_SET_FULL_COUNT:
+       case DRV_TLV_SCSI_STATUS_ACA_ACTIVE_COUNT:
+       case DRV_TLV_SCSI_STATUS_TASK_ABORTED_COUNT:
+       case DRV_TLV_SCSI_CHECK_CONDITION_1_RECEIVED_SK_ASC_ASCQ:
+       case DRV_TLV_SCSI_CHECK_1_TIMESTAMP:
+       case DRV_TLV_SCSI_CHECK_CONDITION_2_RECEIVED_SK_ASC_ASCQ:
+       case DRV_TLV_SCSI_CHECK_2_TIMESTAMP:
+       case DRV_TLV_SCSI_CHECK_CONDITION_3_RECEIVED_SK_ASC_ASCQ:
+       case DRV_TLV_SCSI_CHECK_3_TIMESTAMP:
+       case DRV_TLV_SCSI_CHECK_CONDITION_4_RECEIVED_SK_ASC_ASCQ:
+       case DRV_TLV_SCSI_CHECK_4_TIMESTAMP:
+       case DRV_TLV_SCSI_CHECK_CONDITION_5_RECEIVED_SK_ASC_ASCQ:
+       case DRV_TLV_SCSI_CHECK_5_TIMESTAMP:
+               *tlv_group = QED_MFW_TLV_FCOE;
+               break;
+       case DRV_TLV_TARGET_LLMNR_ENABLED:
+       case DRV_TLV_HEADER_DIGEST_FLAG_ENABLED:
+       case DRV_TLV_DATA_DIGEST_FLAG_ENABLED:
+       case DRV_TLV_AUTHENTICATION_METHOD:
+       case DRV_TLV_ISCSI_BOOT_TARGET_PORTAL:
+       case DRV_TLV_MAX_FRAME_SIZE:
+       case DRV_TLV_PDU_TX_DESCRIPTORS_QUEUE_SIZE:
+       case DRV_TLV_PDU_RX_DESCRIPTORS_QUEUE_SIZE:
+       case DRV_TLV_ISCSI_BOOT_PROGRESS:
+       case DRV_TLV_PDU_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
+       case DRV_TLV_PDU_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
+       case DRV_TLV_ISCSI_PDU_RX_FRAMES_RECEIVED:
+       case DRV_TLV_ISCSI_PDU_RX_BYTES_RECEIVED:
+       case DRV_TLV_ISCSI_PDU_TX_FRAMES_SENT:
+       case DRV_TLV_ISCSI_PDU_TX_BYTES_SENT:
+               *tlv_group |= QED_MFW_TLV_ISCSI;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* Returns size of the data buffer or, -1 in case TLV data is not available. */
+static int
+qed_mfw_get_gen_tlv_value(struct qed_drv_tlv_hdr *p_tlv,
+                         struct qed_mfw_tlv_generic *p_drv_buf,
+                         struct qed_tlv_parsed_buf *p_buf)
+{
+       switch (p_tlv->tlv_type) {
+       case DRV_TLV_FEATURE_FLAGS:
+               if (p_drv_buf->flags.b_set) {
+                       memset(p_buf->data, 0, sizeof(u8) * QED_TLV_DATA_MAX);
+                       p_buf->data[0] = p_drv_buf->flags.ipv4_csum_offload ?
+                           1 : 0;
+                       p_buf->data[0] |= (p_drv_buf->flags.lso_supported ?
+                                          1 : 0) << 1;
+                       p_buf->p_val = p_buf->data;
+                       return QED_MFW_TLV_FLAGS_SIZE;
+               }
+               break;
+
+       case DRV_TLV_LOCAL_ADMIN_ADDR:
+       case DRV_TLV_ADDITIONAL_MAC_ADDR_1:
+       case DRV_TLV_ADDITIONAL_MAC_ADDR_2:
+               {
+                       int idx = p_tlv->tlv_type - DRV_TLV_LOCAL_ADMIN_ADDR;
+
+                       if (p_drv_buf->mac_set[idx]) {
+                               p_buf->p_val = p_drv_buf->mac[idx];
+                               return ETH_ALEN;
+                       }
+                       break;
+               }
+
+       case DRV_TLV_RX_FRAMES_RECEIVED:
+               if (p_drv_buf->rx_frames_set) {
+                       p_buf->p_val = &p_drv_buf->rx_frames;
+                       return sizeof(p_drv_buf->rx_frames);
+               }
+               break;
+       case DRV_TLV_RX_BYTES_RECEIVED:
+               if (p_drv_buf->rx_bytes_set) {
+                       p_buf->p_val = &p_drv_buf->rx_bytes;
+                       return sizeof(p_drv_buf->rx_bytes);
+               }
+               break;
+       case DRV_TLV_TX_FRAMES_SENT:
+               if (p_drv_buf->tx_frames_set) {
+                       p_buf->p_val = &p_drv_buf->tx_frames;
+                       return sizeof(p_drv_buf->tx_frames);
+               }
+               break;
+       case DRV_TLV_TX_BYTES_SENT:
+               if (p_drv_buf->tx_bytes_set) {
+                       p_buf->p_val = &p_drv_buf->tx_bytes;
+                       return sizeof(p_drv_buf->tx_bytes);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return -1;
+}
+
+static int
+qed_mfw_get_eth_tlv_value(struct qed_drv_tlv_hdr *p_tlv,
+                         struct qed_mfw_tlv_eth *p_drv_buf,
+                         struct qed_tlv_parsed_buf *p_buf)
+{
+       switch (p_tlv->tlv_type) {
+       case DRV_TLV_LSO_MAX_OFFLOAD_SIZE:
+               if (p_drv_buf->lso_maxoff_size_set) {
+                       p_buf->p_val = &p_drv_buf->lso_maxoff_size;
+                       return sizeof(p_drv_buf->lso_maxoff_size);
+               }
+               break;
+       case DRV_TLV_LSO_MIN_SEGMENT_COUNT:
+               if (p_drv_buf->lso_minseg_size_set) {
+                       p_buf->p_val = &p_drv_buf->lso_minseg_size;
+                       return sizeof(p_drv_buf->lso_minseg_size);
+               }
+               break;
+       case DRV_TLV_PROMISCUOUS_MODE:
+               if (p_drv_buf->prom_mode_set) {
+                       p_buf->p_val = &p_drv_buf->prom_mode;
+                       return sizeof(p_drv_buf->prom_mode);
+               }
+               break;
+       case DRV_TLV_TX_DESCRIPTORS_QUEUE_SIZE:
+               if (p_drv_buf->tx_descr_size_set) {
+                       p_buf->p_val = &p_drv_buf->tx_descr_size;
+                       return sizeof(p_drv_buf->tx_descr_size);
+               }
+               break;
+       case DRV_TLV_RX_DESCRIPTORS_QUEUE_SIZE:
+               if (p_drv_buf->rx_descr_size_set) {
+                       p_buf->p_val = &p_drv_buf->rx_descr_size;
+                       return sizeof(p_drv_buf->rx_descr_size);
+               }
+               break;
+       case DRV_TLV_NUM_OF_NET_QUEUE_VMQ_CFG:
+               if (p_drv_buf->netq_count_set) {
+                       p_buf->p_val = &p_drv_buf->netq_count;
+                       return sizeof(p_drv_buf->netq_count);
+               }
+               break;
+       case DRV_TLV_NUM_OFFLOADED_CONNECTIONS_TCP_IPV4:
+               if (p_drv_buf->tcp4_offloads_set) {
+                       p_buf->p_val = &p_drv_buf->tcp4_offloads;
+                       return sizeof(p_drv_buf->tcp4_offloads);
+               }
+               break;
+       case DRV_TLV_NUM_OFFLOADED_CONNECTIONS_TCP_IPV6:
+               if (p_drv_buf->tcp6_offloads_set) {
+                       p_buf->p_val = &p_drv_buf->tcp6_offloads;
+                       return sizeof(p_drv_buf->tcp6_offloads);
+               }
+               break;
+       case DRV_TLV_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
+               if (p_drv_buf->tx_descr_qdepth_set) {
+                       p_buf->p_val = &p_drv_buf->tx_descr_qdepth;
+                       return sizeof(p_drv_buf->tx_descr_qdepth);
+               }
+               break;
+       case DRV_TLV_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
+               if (p_drv_buf->rx_descr_qdepth_set) {
+                       p_buf->p_val = &p_drv_buf->rx_descr_qdepth;
+                       return sizeof(p_drv_buf->rx_descr_qdepth);
+               }
+               break;
+       case DRV_TLV_IOV_OFFLOAD:
+               if (p_drv_buf->iov_offload_set) {
+                       p_buf->p_val = &p_drv_buf->iov_offload;
+                       return sizeof(p_drv_buf->iov_offload);
+               }
+               break;
+       case DRV_TLV_TX_QUEUES_EMPTY:
+               if (p_drv_buf->txqs_empty_set) {
+                       p_buf->p_val = &p_drv_buf->txqs_empty;
+                       return sizeof(p_drv_buf->txqs_empty);
+               }
+               break;
+       case DRV_TLV_RX_QUEUES_EMPTY:
+               if (p_drv_buf->rxqs_empty_set) {
+                       p_buf->p_val = &p_drv_buf->rxqs_empty;
+                       return sizeof(p_drv_buf->rxqs_empty);
+               }
+               break;
+       case DRV_TLV_TX_QUEUES_FULL:
+               if (p_drv_buf->num_txqs_full_set) {
+                       p_buf->p_val = &p_drv_buf->num_txqs_full;
+                       return sizeof(p_drv_buf->num_txqs_full);
+               }
+               break;
+       case DRV_TLV_RX_QUEUES_FULL:
+               if (p_drv_buf->num_rxqs_full_set) {
+                       p_buf->p_val = &p_drv_buf->num_rxqs_full;
+                       return sizeof(p_drv_buf->num_rxqs_full);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return -1;
+}
+
+static int
+qed_mfw_get_tlv_time_value(struct qed_mfw_tlv_time *p_time,
+                          struct qed_tlv_parsed_buf *p_buf)
+{
+       if (!p_time->b_set)
+               return -1;
+
+       /* Validate numbers */
+       if (p_time->month > 12)
+               p_time->month = 0;
+       if (p_time->day > 31)
+               p_time->day = 0;
+       if (p_time->hour > 23)
+               p_time->hour = 0;
+       if (p_time->min > 59)
+               p_time->hour = 0;
+       if (p_time->msec > 999)
+               p_time->msec = 0;
+       if (p_time->usec > 999)
+               p_time->usec = 0;
+
+       memset(p_buf->data, 0, sizeof(u8) * QED_TLV_DATA_MAX);
+       snprintf(p_buf->data, 14, "%d%d%d%d%d%d",
+                p_time->month, p_time->day,
+                p_time->hour, p_time->min, p_time->msec, p_time->usec);
+
+       p_buf->p_val = p_buf->data;
+
+       return QED_MFW_TLV_TIME_SIZE;
+}
+
+static int
+qed_mfw_get_fcoe_tlv_value(struct qed_drv_tlv_hdr *p_tlv,
+                          struct qed_mfw_tlv_fcoe *p_drv_buf,
+                          struct qed_tlv_parsed_buf *p_buf)
+{
+       struct qed_mfw_tlv_time *p_time;
+       u8 idx;
+
+       switch (p_tlv->tlv_type) {
+       case DRV_TLV_SCSI_TO:
+               if (p_drv_buf->scsi_timeout_set) {
+                       p_buf->p_val = &p_drv_buf->scsi_timeout;
+                       return sizeof(p_drv_buf->scsi_timeout);
+               }
+               break;
+       case DRV_TLV_R_T_TOV:
+               if (p_drv_buf->rt_tov_set) {
+                       p_buf->p_val = &p_drv_buf->rt_tov;
+                       return sizeof(p_drv_buf->rt_tov);
+               }
+               break;
+       case DRV_TLV_R_A_TOV:
+               if (p_drv_buf->ra_tov_set) {
+                       p_buf->p_val = &p_drv_buf->ra_tov;
+                       return sizeof(p_drv_buf->ra_tov);
+               }
+               break;
+       case DRV_TLV_E_D_TOV:
+               if (p_drv_buf->ed_tov_set) {
+                       p_buf->p_val = &p_drv_buf->ed_tov;
+                       return sizeof(p_drv_buf->ed_tov);
+               }
+               break;
+       case DRV_TLV_CR_TOV:
+               if (p_drv_buf->cr_tov_set) {
+                       p_buf->p_val = &p_drv_buf->cr_tov;
+                       return sizeof(p_drv_buf->cr_tov);
+               }
+               break;
+       case DRV_TLV_BOOT_TYPE:
+               if (p_drv_buf->boot_type_set) {
+                       p_buf->p_val = &p_drv_buf->boot_type;
+                       return sizeof(p_drv_buf->boot_type);
+               }
+               break;
+       case DRV_TLV_NPIV_STATE:
+               if (p_drv_buf->npiv_state_set) {
+                       p_buf->p_val = &p_drv_buf->npiv_state;
+                       return sizeof(p_drv_buf->npiv_state);
+               }
+               break;
+       case DRV_TLV_NUM_OF_NPIV_IDS:
+               if (p_drv_buf->num_npiv_ids_set) {
+                       p_buf->p_val = &p_drv_buf->num_npiv_ids;
+                       return sizeof(p_drv_buf->num_npiv_ids);
+               }
+               break;
+       case DRV_TLV_SWITCH_NAME:
+               if (p_drv_buf->switch_name_set) {
+                       p_buf->p_val = &p_drv_buf->switch_name;
+                       return sizeof(p_drv_buf->switch_name);
+               }
+               break;
+       case DRV_TLV_SWITCH_PORT_NUM:
+               if (p_drv_buf->switch_portnum_set) {
+                       p_buf->p_val = &p_drv_buf->switch_portnum;
+                       return sizeof(p_drv_buf->switch_portnum);
+               }
+               break;
+       case DRV_TLV_SWITCH_PORT_ID:
+               if (p_drv_buf->switch_portid_set) {
+                       p_buf->p_val = &p_drv_buf->switch_portid;
+                       return sizeof(p_drv_buf->switch_portid);
+               }
+               break;
+       case DRV_TLV_VENDOR_NAME:
+               if (p_drv_buf->vendor_name_set) {
+                       p_buf->p_val = &p_drv_buf->vendor_name;
+                       return sizeof(p_drv_buf->vendor_name);
+               }
+               break;
+       case DRV_TLV_SWITCH_MODEL:
+               if (p_drv_buf->switch_model_set) {
+                       p_buf->p_val = &p_drv_buf->switch_model;
+                       return sizeof(p_drv_buf->switch_model);
+               }
+               break;
+       case DRV_TLV_SWITCH_FW_VER:
+               if (p_drv_buf->switch_fw_version_set) {
+                       p_buf->p_val = &p_drv_buf->switch_fw_version;
+                       return sizeof(p_drv_buf->switch_fw_version);
+               }
+               break;
+       case DRV_TLV_QOS_PRIORITY_PER_802_1P:
+               if (p_drv_buf->qos_pri_set) {
+                       p_buf->p_val = &p_drv_buf->qos_pri;
+                       return sizeof(p_drv_buf->qos_pri);
+               }
+               break;
+       case DRV_TLV_PORT_ALIAS:
+               if (p_drv_buf->port_alias_set) {
+                       p_buf->p_val = &p_drv_buf->port_alias;
+                       return sizeof(p_drv_buf->port_alias);
+               }
+               break;
+       case DRV_TLV_PORT_STATE:
+               if (p_drv_buf->port_state_set) {
+                       p_buf->p_val = &p_drv_buf->port_state;
+                       return sizeof(p_drv_buf->port_state);
+               }
+               break;
+       case DRV_TLV_FIP_TX_DESCRIPTORS_QUEUE_SIZE:
+               if (p_drv_buf->fip_tx_descr_size_set) {
+                       p_buf->p_val = &p_drv_buf->fip_tx_descr_size;
+                       return sizeof(p_drv_buf->fip_tx_descr_size);
+               }
+               break;
+       case DRV_TLV_FCOE_RX_DESCRIPTORS_QUEUE_SIZE:
+               if (p_drv_buf->fip_rx_descr_size_set) {
+                       p_buf->p_val = &p_drv_buf->fip_rx_descr_size;
+                       return sizeof(p_drv_buf->fip_rx_descr_size);
+               }
+               break;
+       case DRV_TLV_LINK_FAILURE_COUNT:
+               if (p_drv_buf->link_failures_set) {
+                       p_buf->p_val = &p_drv_buf->link_failures;
+                       return sizeof(p_drv_buf->link_failures);
+               }
+               break;
+       case DRV_TLV_FCOE_BOOT_PROGRESS:
+               if (p_drv_buf->fcoe_boot_progress_set) {
+                       p_buf->p_val = &p_drv_buf->fcoe_boot_progress;
+                       return sizeof(p_drv_buf->fcoe_boot_progress);
+               }
+               break;
+       case DRV_TLV_RX_BROADCAST_PACKETS:
+               if (p_drv_buf->rx_bcast_set) {
+                       p_buf->p_val = &p_drv_buf->rx_bcast;
+                       return sizeof(p_drv_buf->rx_bcast);
+               }
+               break;
+       case DRV_TLV_TX_BROADCAST_PACKETS:
+               if (p_drv_buf->tx_bcast_set) {
+                       p_buf->p_val = &p_drv_buf->tx_bcast;
+                       return sizeof(p_drv_buf->tx_bcast);
+               }
+               break;
+       case DRV_TLV_FCOE_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
+               if (p_drv_buf->fcoe_txq_depth_set) {
+                       p_buf->p_val = &p_drv_buf->fcoe_txq_depth;
+                       return sizeof(p_drv_buf->fcoe_txq_depth);
+               }
+               break;
+       case DRV_TLV_FCOE_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
+               if (p_drv_buf->fcoe_rxq_depth_set) {
+                       p_buf->p_val = &p_drv_buf->fcoe_rxq_depth;
+                       return sizeof(p_drv_buf->fcoe_rxq_depth);
+               }
+               break;
+       case DRV_TLV_FCOE_RX_FRAMES_RECEIVED:
+               if (p_drv_buf->fcoe_rx_frames_set) {
+                       p_buf->p_val = &p_drv_buf->fcoe_rx_frames;
+                       return sizeof(p_drv_buf->fcoe_rx_frames);
+               }
+               break;
+       case DRV_TLV_FCOE_RX_BYTES_RECEIVED:
+               if (p_drv_buf->fcoe_rx_bytes_set) {
+                       p_buf->p_val = &p_drv_buf->fcoe_rx_bytes;
+                       return sizeof(p_drv_buf->fcoe_rx_bytes);
+               }
+               break;
+       case DRV_TLV_FCOE_TX_FRAMES_SENT:
+               if (p_drv_buf->fcoe_tx_frames_set) {
+                       p_buf->p_val = &p_drv_buf->fcoe_tx_frames;
+                       return sizeof(p_drv_buf->fcoe_tx_frames);
+               }
+               break;
+       case DRV_TLV_FCOE_TX_BYTES_SENT:
+               if (p_drv_buf->fcoe_tx_bytes_set) {
+                       p_buf->p_val = &p_drv_buf->fcoe_tx_bytes;
+                       return sizeof(p_drv_buf->fcoe_tx_bytes);
+               }
+               break;
+       case DRV_TLV_CRC_ERROR_COUNT:
+               if (p_drv_buf->crc_count_set) {
+                       p_buf->p_val = &p_drv_buf->crc_count;
+                       return sizeof(p_drv_buf->crc_count);
+               }
+               break;
+       case DRV_TLV_CRC_ERROR_1_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_CRC_ERROR_2_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_CRC_ERROR_3_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_CRC_ERROR_4_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_CRC_ERROR_5_RECEIVED_SOURCE_FC_ID:
+               idx = (p_tlv->tlv_type -
+                      DRV_TLV_CRC_ERROR_1_RECEIVED_SOURCE_FC_ID) / 2;
+
+               if (p_drv_buf->crc_err_src_fcid_set[idx]) {
+                       p_buf->p_val = &p_drv_buf->crc_err_src_fcid[idx];
+                       return sizeof(p_drv_buf->crc_err_src_fcid[idx]);
+               }
+               break;
+       case DRV_TLV_CRC_ERROR_1_TIMESTAMP:
+       case DRV_TLV_CRC_ERROR_2_TIMESTAMP:
+       case DRV_TLV_CRC_ERROR_3_TIMESTAMP:
+       case DRV_TLV_CRC_ERROR_4_TIMESTAMP:
+       case DRV_TLV_CRC_ERROR_5_TIMESTAMP:
+               idx = (p_tlv->tlv_type - DRV_TLV_CRC_ERROR_1_TIMESTAMP) / 2;
+
+               return qed_mfw_get_tlv_time_value(&p_drv_buf->crc_err[idx],
+                                                 p_buf);
+       case DRV_TLV_LOSS_OF_SYNC_ERROR_COUNT:
+               if (p_drv_buf->losync_err_set) {
+                       p_buf->p_val = &p_drv_buf->losync_err;
+                       return sizeof(p_drv_buf->losync_err);
+               }
+               break;
+       case DRV_TLV_LOSS_OF_SIGNAL_ERRORS:
+               if (p_drv_buf->losig_err_set) {
+                       p_buf->p_val = &p_drv_buf->losig_err;
+                       return sizeof(p_drv_buf->losig_err);
+               }
+               break;
+       case DRV_TLV_PRIMITIVE_SEQUENCE_PROTOCOL_ERROR_COUNT:
+               if (p_drv_buf->primtive_err_set) {
+                       p_buf->p_val = &p_drv_buf->primtive_err;
+                       return sizeof(p_drv_buf->primtive_err);
+               }
+               break;
+       case DRV_TLV_DISPARITY_ERROR_COUNT:
+               if (p_drv_buf->disparity_err_set) {
+                       p_buf->p_val = &p_drv_buf->disparity_err;
+                       return sizeof(p_drv_buf->disparity_err);
+               }
+               break;
+       case DRV_TLV_CODE_VIOLATION_ERROR_COUNT:
+               if (p_drv_buf->code_violation_err_set) {
+                       p_buf->p_val = &p_drv_buf->code_violation_err;
+                       return sizeof(p_drv_buf->code_violation_err);
+               }
+               break;
+       case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_1:
+       case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_2:
+       case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_3:
+       case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_4:
+               idx = p_tlv->tlv_type -
+                       DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_1;
+               if (p_drv_buf->flogi_param_set[idx]) {
+                       p_buf->p_val = &p_drv_buf->flogi_param[idx];
+                       return sizeof(p_drv_buf->flogi_param[idx]);
+               }
+               break;
+       case DRV_TLV_LAST_FLOGI_TIMESTAMP:
+               return qed_mfw_get_tlv_time_value(&p_drv_buf->flogi_tstamp,
+                                                 p_buf);
+       case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_1:
+       case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_2:
+       case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_3:
+       case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_4:
+               idx = p_tlv->tlv_type -
+                       DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_1;
+
+               if (p_drv_buf->flogi_acc_param_set[idx]) {
+                       p_buf->p_val = &p_drv_buf->flogi_acc_param[idx];
+                       return sizeof(p_drv_buf->flogi_acc_param[idx]);
+               }
+               break;
+       case DRV_TLV_LAST_FLOGI_ACC_TIMESTAMP:
+               return qed_mfw_get_tlv_time_value(&p_drv_buf->flogi_acc_tstamp,
+                                                 p_buf);
+       case DRV_TLV_LAST_FLOGI_RJT:
+               if (p_drv_buf->flogi_rjt_set) {
+                       p_buf->p_val = &p_drv_buf->flogi_rjt;
+                       return sizeof(p_drv_buf->flogi_rjt);
+               }
+               break;
+       case DRV_TLV_LAST_FLOGI_RJT_TIMESTAMP:
+               return qed_mfw_get_tlv_time_value(&p_drv_buf->flogi_rjt_tstamp,
+                                                 p_buf);
+       case DRV_TLV_FDISCS_SENT_COUNT:
+               if (p_drv_buf->fdiscs_set) {
+                       p_buf->p_val = &p_drv_buf->fdiscs;
+                       return sizeof(p_drv_buf->fdiscs);
+               }
+               break;
+       case DRV_TLV_FDISC_ACCS_RECEIVED:
+               if (p_drv_buf->fdisc_acc_set) {
+                       p_buf->p_val = &p_drv_buf->fdisc_acc;
+                       return sizeof(p_drv_buf->fdisc_acc);
+               }
+               break;
+       case DRV_TLV_FDISC_RJTS_RECEIVED:
+               if (p_drv_buf->fdisc_rjt_set) {
+                       p_buf->p_val = &p_drv_buf->fdisc_rjt;
+                       return sizeof(p_drv_buf->fdisc_rjt);
+               }
+               break;
+       case DRV_TLV_PLOGI_SENT_COUNT:
+               if (p_drv_buf->plogi_set) {
+                       p_buf->p_val = &p_drv_buf->plogi;
+                       return sizeof(p_drv_buf->plogi);
+               }
+               break;
+       case DRV_TLV_PLOGI_ACCS_RECEIVED:
+               if (p_drv_buf->plogi_acc_set) {
+                       p_buf->p_val = &p_drv_buf->plogi_acc;
+                       return sizeof(p_drv_buf->plogi_acc);
+               }
+               break;
+       case DRV_TLV_PLOGI_RJTS_RECEIVED:
+               if (p_drv_buf->plogi_rjt_set) {
+                       p_buf->p_val = &p_drv_buf->plogi_rjt;
+                       return sizeof(p_drv_buf->plogi_rjt);
+               }
+               break;
+       case DRV_TLV_PLOGI_1_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_PLOGI_2_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_PLOGI_3_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_PLOGI_4_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_PLOGI_5_SENT_DESTINATION_FC_ID:
+               idx = (p_tlv->tlv_type -
+                      DRV_TLV_PLOGI_1_SENT_DESTINATION_FC_ID) / 2;
+
+               if (p_drv_buf->plogi_dst_fcid_set[idx]) {
+                       p_buf->p_val = &p_drv_buf->plogi_dst_fcid[idx];
+                       return sizeof(p_drv_buf->plogi_dst_fcid[idx]);
+               }
+               break;
+       case DRV_TLV_PLOGI_1_TIMESTAMP:
+       case DRV_TLV_PLOGI_2_TIMESTAMP:
+       case DRV_TLV_PLOGI_3_TIMESTAMP:
+       case DRV_TLV_PLOGI_4_TIMESTAMP:
+       case DRV_TLV_PLOGI_5_TIMESTAMP:
+               idx = (p_tlv->tlv_type - DRV_TLV_PLOGI_1_TIMESTAMP) / 2;
+
+               return qed_mfw_get_tlv_time_value(&p_drv_buf->plogi_tstamp[idx],
+                                                 p_buf);
+       case DRV_TLV_PLOGI_1_ACC_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_PLOGI_2_ACC_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_PLOGI_3_ACC_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_PLOGI_4_ACC_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_PLOGI_5_ACC_RECEIVED_SOURCE_FC_ID:
+               idx = (p_tlv->tlv_type -
+                      DRV_TLV_PLOGI_1_ACC_RECEIVED_SOURCE_FC_ID) / 2;
+
+               if (p_drv_buf->plogi_acc_src_fcid_set[idx]) {
+                       p_buf->p_val = &p_drv_buf->plogi_acc_src_fcid[idx];
+                       return sizeof(p_drv_buf->plogi_acc_src_fcid[idx]);
+               }
+               break;
+       case DRV_TLV_PLOGI_1_ACC_TIMESTAMP:
+       case DRV_TLV_PLOGI_2_ACC_TIMESTAMP:
+       case DRV_TLV_PLOGI_3_ACC_TIMESTAMP:
+       case DRV_TLV_PLOGI_4_ACC_TIMESTAMP:
+       case DRV_TLV_PLOGI_5_ACC_TIMESTAMP:
+               idx = (p_tlv->tlv_type - DRV_TLV_PLOGI_1_ACC_TIMESTAMP) / 2;
+               p_time = &p_drv_buf->plogi_acc_tstamp[idx];
+
+               return qed_mfw_get_tlv_time_value(p_time, p_buf);
+       case DRV_TLV_LOGOS_ISSUED:
+               if (p_drv_buf->tx_plogos_set) {
+                       p_buf->p_val = &p_drv_buf->tx_plogos;
+                       return sizeof(p_drv_buf->tx_plogos);
+               }
+               break;
+       case DRV_TLV_LOGO_ACCS_RECEIVED:
+               if (p_drv_buf->plogo_acc_set) {
+                       p_buf->p_val = &p_drv_buf->plogo_acc;
+                       return sizeof(p_drv_buf->plogo_acc);
+               }
+               break;
+       case DRV_TLV_LOGO_RJTS_RECEIVED:
+               if (p_drv_buf->plogo_rjt_set) {
+                       p_buf->p_val = &p_drv_buf->plogo_rjt;
+                       return sizeof(p_drv_buf->plogo_rjt);
+               }
+               break;
+       case DRV_TLV_LOGO_1_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_LOGO_2_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_LOGO_3_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_LOGO_4_RECEIVED_SOURCE_FC_ID:
+       case DRV_TLV_LOGO_5_RECEIVED_SOURCE_FC_ID:
+               idx = (p_tlv->tlv_type - DRV_TLV_LOGO_1_RECEIVED_SOURCE_FC_ID) /
+                       2;
+
+               if (p_drv_buf->plogo_src_fcid_set[idx]) {
+                       p_buf->p_val = &p_drv_buf->plogo_src_fcid[idx];
+                       return sizeof(p_drv_buf->plogo_src_fcid[idx]);
+               }
+               break;
+       case DRV_TLV_LOGO_1_TIMESTAMP:
+       case DRV_TLV_LOGO_2_TIMESTAMP:
+       case DRV_TLV_LOGO_3_TIMESTAMP:
+       case DRV_TLV_LOGO_4_TIMESTAMP:
+       case DRV_TLV_LOGO_5_TIMESTAMP:
+               idx = (p_tlv->tlv_type - DRV_TLV_LOGO_1_TIMESTAMP) / 2;
+
+               return qed_mfw_get_tlv_time_value(&p_drv_buf->plogo_tstamp[idx],
+                                                 p_buf);
+       case DRV_TLV_LOGOS_RECEIVED:
+               if (p_drv_buf->rx_logos_set) {
+                       p_buf->p_val = &p_drv_buf->rx_logos;
+                       return sizeof(p_drv_buf->rx_logos);
+               }
+               break;
+       case DRV_TLV_ACCS_ISSUED:
+               if (p_drv_buf->tx_accs_set) {
+                       p_buf->p_val = &p_drv_buf->tx_accs;
+                       return sizeof(p_drv_buf->tx_accs);
+               }
+               break;
+       case DRV_TLV_PRLIS_ISSUED:
+               if (p_drv_buf->tx_prlis_set) {
+                       p_buf->p_val = &p_drv_buf->tx_prlis;
+                       return sizeof(p_drv_buf->tx_prlis);
+               }
+               break;
+       case DRV_TLV_ACCS_RECEIVED:
+               if (p_drv_buf->rx_accs_set) {
+                       p_buf->p_val = &p_drv_buf->rx_accs;
+                       return sizeof(p_drv_buf->rx_accs);
+               }
+               break;
+       case DRV_TLV_ABTS_SENT_COUNT:
+               if (p_drv_buf->tx_abts_set) {
+                       p_buf->p_val = &p_drv_buf->tx_abts;
+                       return sizeof(p_drv_buf->tx_abts);
+               }
+               break;
+       case DRV_TLV_ABTS_ACCS_RECEIVED:
+               if (p_drv_buf->rx_abts_acc_set) {
+                       p_buf->p_val = &p_drv_buf->rx_abts_acc;
+                       return sizeof(p_drv_buf->rx_abts_acc);
+               }
+               break;
+       case DRV_TLV_ABTS_RJTS_RECEIVED:
+               if (p_drv_buf->rx_abts_rjt_set) {
+                       p_buf->p_val = &p_drv_buf->rx_abts_rjt;
+                       return sizeof(p_drv_buf->rx_abts_rjt);
+               }
+               break;
+       case DRV_TLV_ABTS_1_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_ABTS_2_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_ABTS_3_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_ABTS_4_SENT_DESTINATION_FC_ID:
+       case DRV_TLV_ABTS_5_SENT_DESTINATION_FC_ID:
+               idx = (p_tlv->tlv_type -
+                      DRV_TLV_ABTS_1_SENT_DESTINATION_FC_ID) / 2;
+
+               if (p_drv_buf->abts_dst_fcid_set[idx]) {
+                       p_buf->p_val = &p_drv_buf->abts_dst_fcid[idx];
+                       return sizeof(p_drv_buf->abts_dst_fcid[idx]);
+               }
+               break;
+       case DRV_TLV_ABTS_1_TIMESTAMP:
+       case DRV_TLV_ABTS_2_TIMESTAMP:
+       case DRV_TLV_ABTS_3_TIMESTAMP:
+       case DRV_TLV_ABTS_4_TIMESTAMP:
+       case DRV_TLV_ABTS_5_TIMESTAMP:
+               idx = (p_tlv->tlv_type - DRV_TLV_ABTS_1_TIMESTAMP) / 2;
+
+               return qed_mfw_get_tlv_time_value(&p_drv_buf->abts_tstamp[idx],
+                                                 p_buf);
+       case DRV_TLV_RSCNS_RECEIVED:
+               if (p_drv_buf->rx_rscn_set) {
+                       p_buf->p_val = &p_drv_buf->rx_rscn;
+                       return sizeof(p_drv_buf->rx_rscn);
+               }
+               break;
+       case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_1:
+       case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_2:
+       case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_3:
+       case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_4:
+               idx = p_tlv->tlv_type - DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_1;
+
+               if (p_drv_buf->rx_rscn_nport_set[idx]) {
+                       p_buf->p_val = &p_drv_buf->rx_rscn_nport[idx];
+                       return sizeof(p_drv_buf->rx_rscn_nport[idx]);
+               }
+               break;
+       case DRV_TLV_LUN_RESETS_ISSUED:
+               if (p_drv_buf->tx_lun_rst_set) {
+                       p_buf->p_val = &p_drv_buf->tx_lun_rst;
+                       return sizeof(p_drv_buf->tx_lun_rst);
+               }
+               break;
+       case DRV_TLV_ABORT_TASK_SETS_ISSUED:
+               if (p_drv_buf->abort_task_sets_set) {
+                       p_buf->p_val = &p_drv_buf->abort_task_sets;
+                       return sizeof(p_drv_buf->abort_task_sets);
+               }
+               break;
+       case DRV_TLV_TPRLOS_SENT:
+               if (p_drv_buf->tx_tprlos_set) {
+                       p_buf->p_val = &p_drv_buf->tx_tprlos;
+                       return sizeof(p_drv_buf->tx_tprlos);
+               }
+               break;
+       case DRV_TLV_NOS_SENT_COUNT:
+               if (p_drv_buf->tx_nos_set) {
+                       p_buf->p_val = &p_drv_buf->tx_nos;
+                       return sizeof(p_drv_buf->tx_nos);
+               }
+               break;
+       case DRV_TLV_NOS_RECEIVED_COUNT:
+               if (p_drv_buf->rx_nos_set) {
+                       p_buf->p_val = &p_drv_buf->rx_nos;
+                       return sizeof(p_drv_buf->rx_nos);
+               }
+               break;
+       case DRV_TLV_OLS_COUNT:
+               if (p_drv_buf->ols_set) {
+                       p_buf->p_val = &p_drv_buf->ols;
+                       return sizeof(p_drv_buf->ols);
+               }
+               break;
+       case DRV_TLV_LR_COUNT:
+               if (p_drv_buf->lr_set) {
+                       p_buf->p_val = &p_drv_buf->lr;
+                       return sizeof(p_drv_buf->lr);
+               }
+               break;
+       case DRV_TLV_LRR_COUNT:
+               if (p_drv_buf->lrr_set) {
+                       p_buf->p_val = &p_drv_buf->lrr;
+                       return sizeof(p_drv_buf->lrr);
+               }
+               break;
+       case DRV_TLV_LIP_SENT_COUNT:
+               if (p_drv_buf->tx_lip_set) {
+                       p_buf->p_val = &p_drv_buf->tx_lip;
+                       return sizeof(p_drv_buf->tx_lip);
+               }
+               break;
+       case DRV_TLV_LIP_RECEIVED_COUNT:
+               if (p_drv_buf->rx_lip_set) {
+                       p_buf->p_val = &p_drv_buf->rx_lip;
+                       return sizeof(p_drv_buf->rx_lip);
+               }
+               break;
+       case DRV_TLV_EOFA_COUNT:
+               if (p_drv_buf->eofa_set) {
+                       p_buf->p_val = &p_drv_buf->eofa;
+                       return sizeof(p_drv_buf->eofa);
+               }
+               break;
+       case DRV_TLV_EOFNI_COUNT:
+               if (p_drv_buf->eofni_set) {
+                       p_buf->p_val = &p_drv_buf->eofni;
+                       return sizeof(p_drv_buf->eofni);
+               }
+               break;
+       case DRV_TLV_SCSI_STATUS_CHECK_CONDITION_COUNT:
+               if (p_drv_buf->scsi_chks_set) {
+                       p_buf->p_val = &p_drv_buf->scsi_chks;
+                       return sizeof(p_drv_buf->scsi_chks);
+               }
+               break;
+       case DRV_TLV_SCSI_STATUS_CONDITION_MET_COUNT:
+               if (p_drv_buf->scsi_cond_met_set) {
+                       p_buf->p_val = &p_drv_buf->scsi_cond_met;
+                       return sizeof(p_drv_buf->scsi_cond_met);
+               }
+               break;
+       case DRV_TLV_SCSI_STATUS_BUSY_COUNT:
+               if (p_drv_buf->scsi_busy_set) {
+                       p_buf->p_val = &p_drv_buf->scsi_busy;
+                       return sizeof(p_drv_buf->scsi_busy);
+               }
+               break;
+       case DRV_TLV_SCSI_STATUS_INTERMEDIATE_COUNT:
+               if (p_drv_buf->scsi_inter_set) {
+                       p_buf->p_val = &p_drv_buf->scsi_inter;
+                       return sizeof(p_drv_buf->scsi_inter);
+               }
+               break;
+       case DRV_TLV_SCSI_STATUS_INTERMEDIATE_CONDITION_MET_COUNT:
+               if (p_drv_buf->scsi_inter_cond_met_set) {
+                       p_buf->p_val = &p_drv_buf->scsi_inter_cond_met;
+                       return sizeof(p_drv_buf->scsi_inter_cond_met);
+               }
+               break;
+       case DRV_TLV_SCSI_STATUS_RESERVATION_CONFLICT_COUNT:
+               if (p_drv_buf->scsi_rsv_conflicts_set) {
+                       p_buf->p_val = &p_drv_buf->scsi_rsv_conflicts;
+                       return sizeof(p_drv_buf->scsi_rsv_conflicts);
+               }
+               break;
+       case DRV_TLV_SCSI_STATUS_TASK_SET_FULL_COUNT:
+               if (p_drv_buf->scsi_tsk_full_set) {
+                       p_buf->p_val = &p_drv_buf->scsi_tsk_full;
+                       return sizeof(p_drv_buf->scsi_tsk_full);
+               }
+               break;
+       case DRV_TLV_SCSI_STATUS_ACA_ACTIVE_COUNT:
+               if (p_drv_buf->scsi_aca_active_set) {
+                       p_buf->p_val = &p_drv_buf->scsi_aca_active;
+                       return sizeof(p_drv_buf->scsi_aca_active);
+               }
+               break;
+       case DRV_TLV_SCSI_STATUS_TASK_ABORTED_COUNT:
+               if (p_drv_buf->scsi_tsk_abort_set) {
+                       p_buf->p_val = &p_drv_buf->scsi_tsk_abort;
+                       return sizeof(p_drv_buf->scsi_tsk_abort);
+               }
+               break;
+       case DRV_TLV_SCSI_CHECK_CONDITION_1_RECEIVED_SK_ASC_ASCQ:
+       case DRV_TLV_SCSI_CHECK_CONDITION_2_RECEIVED_SK_ASC_ASCQ:
+       case DRV_TLV_SCSI_CHECK_CONDITION_3_RECEIVED_SK_ASC_ASCQ:
+       case DRV_TLV_SCSI_CHECK_CONDITION_4_RECEIVED_SK_ASC_ASCQ:
+       case DRV_TLV_SCSI_CHECK_CONDITION_5_RECEIVED_SK_ASC_ASCQ:
+               idx = (p_tlv->tlv_type -
+                      DRV_TLV_SCSI_CHECK_CONDITION_1_RECEIVED_SK_ASC_ASCQ) / 2;
+
+               if (p_drv_buf->scsi_rx_chk_set[idx]) {
+                       p_buf->p_val = &p_drv_buf->scsi_rx_chk[idx];
+                       return sizeof(p_drv_buf->scsi_rx_chk[idx]);
+               }
+               break;
+       case DRV_TLV_SCSI_CHECK_1_TIMESTAMP:
+       case DRV_TLV_SCSI_CHECK_2_TIMESTAMP:
+       case DRV_TLV_SCSI_CHECK_3_TIMESTAMP:
+       case DRV_TLV_SCSI_CHECK_4_TIMESTAMP:
+       case DRV_TLV_SCSI_CHECK_5_TIMESTAMP:
+               idx = (p_tlv->tlv_type - DRV_TLV_SCSI_CHECK_1_TIMESTAMP) / 2;
+               p_time = &p_drv_buf->scsi_chk_tstamp[idx];
+
+               return qed_mfw_get_tlv_time_value(p_time, p_buf);
+       default:
+               break;
+       }
+
+       return -1;
+}
+
+static int
+qed_mfw_get_iscsi_tlv_value(struct qed_drv_tlv_hdr *p_tlv,
+                           struct qed_mfw_tlv_iscsi *p_drv_buf,
+                           struct qed_tlv_parsed_buf *p_buf)
+{
+       switch (p_tlv->tlv_type) {
+       case DRV_TLV_TARGET_LLMNR_ENABLED:
+               if (p_drv_buf->target_llmnr_set) {
+                       p_buf->p_val = &p_drv_buf->target_llmnr;
+                       return sizeof(p_drv_buf->target_llmnr);
+               }
+               break;
+       case DRV_TLV_HEADER_DIGEST_FLAG_ENABLED:
+               if (p_drv_buf->header_digest_set) {
+                       p_buf->p_val = &p_drv_buf->header_digest;
+                       return sizeof(p_drv_buf->header_digest);
+               }
+               break;
+       case DRV_TLV_DATA_DIGEST_FLAG_ENABLED:
+               if (p_drv_buf->data_digest_set) {
+                       p_buf->p_val = &p_drv_buf->data_digest;
+                       return sizeof(p_drv_buf->data_digest);
+               }
+               break;
+       case DRV_TLV_AUTHENTICATION_METHOD:
+               if (p_drv_buf->auth_method_set) {
+                       p_buf->p_val = &p_drv_buf->auth_method;
+                       return sizeof(p_drv_buf->auth_method);
+               }
+               break;
+       case DRV_TLV_ISCSI_BOOT_TARGET_PORTAL:
+               if (p_drv_buf->boot_taget_portal_set) {
+                       p_buf->p_val = &p_drv_buf->boot_taget_portal;
+                       return sizeof(p_drv_buf->boot_taget_portal);
+               }
+               break;
+       case DRV_TLV_MAX_FRAME_SIZE:
+               if (p_drv_buf->frame_size_set) {
+                       p_buf->p_val = &p_drv_buf->frame_size;
+                       return sizeof(p_drv_buf->frame_size);
+               }
+               break;
+       case DRV_TLV_PDU_TX_DESCRIPTORS_QUEUE_SIZE:
+               if (p_drv_buf->tx_desc_size_set) {
+                       p_buf->p_val = &p_drv_buf->tx_desc_size;
+                       return sizeof(p_drv_buf->tx_desc_size);
+               }
+               break;
+       case DRV_TLV_PDU_RX_DESCRIPTORS_QUEUE_SIZE:
+               if (p_drv_buf->rx_desc_size_set) {
+                       p_buf->p_val = &p_drv_buf->rx_desc_size;
+                       return sizeof(p_drv_buf->rx_desc_size);
+               }
+               break;
+       case DRV_TLV_ISCSI_BOOT_PROGRESS:
+               if (p_drv_buf->boot_progress_set) {
+                       p_buf->p_val = &p_drv_buf->boot_progress;
+                       return sizeof(p_drv_buf->boot_progress);
+               }
+               break;
+       case DRV_TLV_PDU_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
+               if (p_drv_buf->tx_desc_qdepth_set) {
+                       p_buf->p_val = &p_drv_buf->tx_desc_qdepth;
+                       return sizeof(p_drv_buf->tx_desc_qdepth);
+               }
+               break;
+       case DRV_TLV_PDU_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
+               if (p_drv_buf->rx_desc_qdepth_set) {
+                       p_buf->p_val = &p_drv_buf->rx_desc_qdepth;
+                       return sizeof(p_drv_buf->rx_desc_qdepth);
+               }
+               break;
+       case DRV_TLV_ISCSI_PDU_RX_FRAMES_RECEIVED:
+               if (p_drv_buf->rx_frames_set) {
+                       p_buf->p_val = &p_drv_buf->rx_frames;
+                       return sizeof(p_drv_buf->rx_frames);
+               }
+               break;
+       case DRV_TLV_ISCSI_PDU_RX_BYTES_RECEIVED:
+               if (p_drv_buf->rx_bytes_set) {
+                       p_buf->p_val = &p_drv_buf->rx_bytes;
+                       return sizeof(p_drv_buf->rx_bytes);
+               }
+               break;
+       case DRV_TLV_ISCSI_PDU_TX_FRAMES_SENT:
+               if (p_drv_buf->tx_frames_set) {
+                       p_buf->p_val = &p_drv_buf->tx_frames;
+                       return sizeof(p_drv_buf->tx_frames);
+               }
+               break;
+       case DRV_TLV_ISCSI_PDU_TX_BYTES_SENT:
+               if (p_drv_buf->tx_bytes_set) {
+                       p_buf->p_val = &p_drv_buf->tx_bytes;
+                       return sizeof(p_drv_buf->tx_bytes);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return -1;
+}
+
+static int qed_mfw_update_tlvs(struct qed_hwfn *p_hwfn,
+                              u8 tlv_group, u8 *p_mfw_buf, u32 size)
+{
+       union qed_mfw_tlv_data *p_tlv_data;
+       struct qed_tlv_parsed_buf buffer;
+       struct qed_drv_tlv_hdr tlv;
+       int len = 0;
+       u32 offset;
+       u8 *p_tlv;
+
+       p_tlv_data = vzalloc(sizeof(*p_tlv_data));
+       if (!p_tlv_data)
+               return -ENOMEM;
+
+       if (qed_mfw_fill_tlv_data(p_hwfn, tlv_group, p_tlv_data)) {
+               vfree(p_tlv_data);
+               return -EINVAL;
+       }
+
+       memset(&tlv, 0, sizeof(tlv));
+       for (offset = 0; offset < size;
+            offset += sizeof(tlv) + sizeof(u32) * tlv.tlv_length) {
+               p_tlv = &p_mfw_buf[offset];
+               tlv.tlv_type = TLV_TYPE(p_tlv);
+               tlv.tlv_length = TLV_LENGTH(p_tlv);
+               tlv.tlv_flags = TLV_FLAGS(p_tlv);
+
+               DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                          "Type %d length = %d flags = 0x%x\n", tlv.tlv_type,
+                          tlv.tlv_length, tlv.tlv_flags);
+
+               if (tlv_group == QED_MFW_TLV_GENERIC)
+                       len = qed_mfw_get_gen_tlv_value(&tlv,
+                                                       &p_tlv_data->generic,
+                                                       &buffer);
+               else if (tlv_group == QED_MFW_TLV_ETH)
+                       len = qed_mfw_get_eth_tlv_value(&tlv,
+                                                       &p_tlv_data->eth,
+                                                       &buffer);
+               else if (tlv_group == QED_MFW_TLV_FCOE)
+                       len = qed_mfw_get_fcoe_tlv_value(&tlv,
+                                                        &p_tlv_data->fcoe,
+                                                        &buffer);
+               else
+                       len = qed_mfw_get_iscsi_tlv_value(&tlv,
+                                                         &p_tlv_data->iscsi,
+                                                         &buffer);
+
+               if (len > 0) {
+                       WARN(len > 4 * tlv.tlv_length,
+                            "Incorrect MFW TLV length %d, it shouldn't be greater than %d\n",
+                            len, 4 * tlv.tlv_length);
+                       len = min_t(int, len, 4 * tlv.tlv_length);
+                       tlv.tlv_flags |= QED_DRV_TLV_FLAGS_CHANGED;
+                       TLV_FLAGS(p_tlv) = tlv.tlv_flags;
+                       memcpy(p_mfw_buf + offset + sizeof(tlv),
+                              buffer.p_val, len);
+               }
+       }
+
+       vfree(p_tlv_data);
+
+       return 0;
+}
+
+int qed_mfw_process_tlv_req(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
+{
+       u32 addr, size, offset, resp, param, val, global_offsize, global_addr;
+       u8 tlv_group = 0, id, *p_mfw_buf = NULL, *p_temp;
+       struct qed_drv_tlv_hdr tlv;
+       int rc;
+
+       addr = SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info->public_base,
+                                   PUBLIC_GLOBAL);
+       global_offsize = qed_rd(p_hwfn, p_ptt, addr);
+       global_addr = SECTION_ADDR(global_offsize, 0);
+       addr = global_addr + offsetof(struct public_global, data_ptr);
+       addr = qed_rd(p_hwfn, p_ptt, addr);
+       size = qed_rd(p_hwfn, p_ptt, global_addr +
+                     offsetof(struct public_global, data_size));
+
+       if (!size) {
+               DP_NOTICE(p_hwfn, "Invalid TLV req size = %d\n", size);
+               goto drv_done;
+       }
+
+       p_mfw_buf = vzalloc(size);
+       if (!p_mfw_buf) {
+               DP_NOTICE(p_hwfn, "Failed allocate memory for p_mfw_buf\n");
+               goto drv_done;
+       }
+
+       /* Read the TLV request to local buffer. MFW represents the TLV in
+        * little endian format and mcp returns it bigendian format. Hence
+        * driver need to convert data to little endian first and then do the
+        * memcpy (casting) to preserve the MFW TLV format in the driver buffer.
+        *
+        */
+       for (offset = 0; offset < size; offset += sizeof(u32)) {
+               val = qed_rd(p_hwfn, p_ptt, addr + offset);
+               val = be32_to_cpu(val);
+               memcpy(&p_mfw_buf[offset], &val, sizeof(u32));
+       }
+
+       /* Parse the headers to enumerate the requested TLV groups */
+       for (offset = 0; offset < size;
+            offset += sizeof(tlv) + sizeof(u32) * tlv.tlv_length) {
+               p_temp = &p_mfw_buf[offset];
+               tlv.tlv_type = TLV_TYPE(p_temp);
+               tlv.tlv_length = TLV_LENGTH(p_temp);
+               if (qed_mfw_get_tlv_group(tlv.tlv_type, &tlv_group))
+                       DP_VERBOSE(p_hwfn, NETIF_MSG_DRV,
+                                  "Un recognized TLV %d\n", tlv.tlv_type);
+       }
+
+       /* Sanitize the TLV groups according to personality */
+       if ((tlv_group & QED_MFW_TLV_ETH) && !QED_IS_L2_PERSONALITY(p_hwfn)) {
+               DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                          "Skipping L2 TLVs for non-L2 function\n");
+               tlv_group &= ~QED_MFW_TLV_ETH;
+       }
+
+       if ((tlv_group & QED_MFW_TLV_FCOE) &&
+           p_hwfn->hw_info.personality != QED_PCI_FCOE) {
+               DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                          "Skipping FCoE TLVs for non-FCoE function\n");
+               tlv_group &= ~QED_MFW_TLV_FCOE;
+       }
+
+       if ((tlv_group & QED_MFW_TLV_ISCSI) &&
+           p_hwfn->hw_info.personality != QED_PCI_ISCSI) {
+               DP_VERBOSE(p_hwfn, QED_MSG_SP,
+                          "Skipping iSCSI TLVs for non-iSCSI function\n");
+               tlv_group &= ~QED_MFW_TLV_ISCSI;
+       }
+
+       /* Update the TLV values in the local buffer */
+       for (id = QED_MFW_TLV_GENERIC; id < QED_MFW_TLV_MAX; id <<= 1) {
+               if (tlv_group & id)
+                       if (qed_mfw_update_tlvs(p_hwfn, id, p_mfw_buf, size))
+                               goto drv_done;
+       }
+
+       /* Write the TLV data to shared memory. The stream of 4 bytes first need
+        * to be mem-copied to u32 element to make it as LSB format. And then
+        * converted to big endian as required by mcp-write.
+        */
+       for (offset = 0; offset < size; offset += sizeof(u32)) {
+               memcpy(&val, &p_mfw_buf[offset], sizeof(u32));
+               val = cpu_to_be32(val);
+               qed_wr(p_hwfn, p_ptt, addr + offset, val);
+       }
+
+drv_done:
+       rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_GET_TLV_DONE, 0, &resp,
+                        &param);
+
+       vfree(p_mfw_buf);
+
+       return rc;
+}
index 9935978..2d3f09e 100644 (file)
@@ -290,15 +290,12 @@ struct qede_agg_info {
         * aggregation.
         */
        struct sw_rx_data buffer;
-       dma_addr_t buffer_mapping;
-
        struct sk_buff *skb;
 
        /* We need some structs from the start cookie until termination */
        u16 vlan_tag;
-       u16 start_cqe_bd_len;
-       u8 start_cqe_placement_offset;
 
+       bool tpa_start_fail;
        u8 state;
        u8 frag_id;
 
index ecbf1de..8c6fdad 100644 (file)
@@ -1508,7 +1508,8 @@ static int qede_selftest_receive_traffic(struct qede_dev *edev)
                len =  le16_to_cpu(fp_cqe->len_on_first_bd);
                data_ptr = (u8 *)(page_address(sw_rx_data->data) +
                                  fp_cqe->placement_offset +
-                                 sw_rx_data->page_offset);
+                                 sw_rx_data->page_offset +
+                                 rxq->rx_headroom);
                if (ether_addr_equal(data_ptr,  edev->ndev->dev_addr) &&
                    ether_addr_equal(data_ptr + ETH_ALEN,
                                     edev->ndev->dev_addr)) {
index 1494130..6c70239 100644 (file)
@@ -660,7 +660,8 @@ static int qede_fill_frag_skb(struct qede_dev *edev,
 
        /* Add one frag and update the appropriate fields in the skb */
        skb_fill_page_desc(skb, tpa_info->frag_id++,
-                          current_bd->data, current_bd->page_offset,
+                          current_bd->data,
+                          current_bd->page_offset + rxq->rx_headroom,
                           len_on_bd);
 
        if (unlikely(qede_realloc_rx_buffer(rxq, current_bd))) {
@@ -671,8 +672,7 @@ static int qede_fill_frag_skb(struct qede_dev *edev,
                goto out;
        }
 
-       qed_chain_consume(&rxq->rx_bd_ring);
-       rxq->sw_rx_cons++;
+       qede_rx_bd_ring_consume(rxq);
 
        skb->data_len += len_on_bd;
        skb->truesize += rxq->rx_buf_seg_size;
@@ -721,64 +721,129 @@ static u8 qede_check_tunn_csum(u16 flag)
        return QEDE_CSUM_UNNECESSARY | tcsum;
 }
 
+static inline struct sk_buff *
+qede_build_skb(struct qede_rx_queue *rxq,
+              struct sw_rx_data *bd, u16 len, u16 pad)
+{
+       struct sk_buff *skb;
+       void *buf;
+
+       buf = page_address(bd->data) + bd->page_offset;
+       skb = build_skb(buf, rxq->rx_buf_seg_size);
+
+       skb_reserve(skb, pad);
+       skb_put(skb, len);
+
+       return skb;
+}
+
+static struct sk_buff *
+qede_tpa_rx_build_skb(struct qede_dev *edev,
+                     struct qede_rx_queue *rxq,
+                     struct sw_rx_data *bd, u16 len, u16 pad,
+                     bool alloc_skb)
+{
+       struct sk_buff *skb;
+
+       skb = qede_build_skb(rxq, bd, len, pad);
+       bd->page_offset += rxq->rx_buf_seg_size;
+
+       if (bd->page_offset == PAGE_SIZE) {
+               if (unlikely(qede_alloc_rx_buffer(rxq, true))) {
+                       DP_NOTICE(edev,
+                                 "Failed to allocate RX buffer for tpa start\n");
+                       bd->page_offset -= rxq->rx_buf_seg_size;
+                       page_ref_inc(bd->data);
+                       dev_kfree_skb_any(skb);
+                       return NULL;
+               }
+       } else {
+               page_ref_inc(bd->data);
+               qede_reuse_page(rxq, bd);
+       }
+
+       /* We've consumed the first BD and prepared an SKB */
+       qede_rx_bd_ring_consume(rxq);
+
+       return skb;
+}
+
+static struct sk_buff *
+qede_rx_build_skb(struct qede_dev *edev,
+                 struct qede_rx_queue *rxq,
+                 struct sw_rx_data *bd, u16 len, u16 pad)
+{
+       struct sk_buff *skb = NULL;
+
+       /* For smaller frames still need to allocate skb, memcpy
+        * data and benefit in reusing the page segment instead of
+        * un-mapping it.
+        */
+       if ((len + pad <= edev->rx_copybreak)) {
+               unsigned int offset = bd->page_offset + pad;
+
+               skb = netdev_alloc_skb(edev->ndev, QEDE_RX_HDR_SIZE);
+               if (unlikely(!skb))
+                       return NULL;
+
+               skb_reserve(skb, pad);
+               memcpy(skb_put(skb, len),
+                      page_address(bd->data) + offset, len);
+               qede_reuse_page(rxq, bd);
+               goto out;
+       }
+
+       skb = qede_build_skb(rxq, bd, len, pad);
+
+       if (unlikely(qede_realloc_rx_buffer(rxq, bd))) {
+               /* Incr page ref count to reuse on allocation failure so
+                * that it doesn't get freed while freeing SKB [as its
+                * already mapped there].
+                */
+               page_ref_inc(bd->data);
+               dev_kfree_skb_any(skb);
+               return NULL;
+       }
+out:
+       /* We've consumed the first BD and prepared an SKB */
+       qede_rx_bd_ring_consume(rxq);
+
+       return skb;
+}
+
 static void qede_tpa_start(struct qede_dev *edev,
                           struct qede_rx_queue *rxq,
                           struct eth_fast_path_rx_tpa_start_cqe *cqe)
 {
        struct qede_agg_info *tpa_info = &rxq->tpa_info[cqe->tpa_agg_index];
-       struct eth_rx_bd *rx_bd_cons = qed_chain_consume(&rxq->rx_bd_ring);
-       struct eth_rx_bd *rx_bd_prod = qed_chain_produce(&rxq->rx_bd_ring);
-       struct sw_rx_data *replace_buf = &tpa_info->buffer;
-       dma_addr_t mapping = tpa_info->buffer_mapping;
        struct sw_rx_data *sw_rx_data_cons;
-       struct sw_rx_data *sw_rx_data_prod;
+       u16 pad;
 
        sw_rx_data_cons = &rxq->sw_rx_ring[rxq->sw_rx_cons & NUM_RX_BDS_MAX];
-       sw_rx_data_prod = &rxq->sw_rx_ring[rxq->sw_rx_prod & NUM_RX_BDS_MAX];
+       pad = cqe->placement_offset + rxq->rx_headroom;
 
-       /* Use pre-allocated replacement buffer - we can't release the agg.
-        * start until its over and we don't want to risk allocation failing
-        * here, so re-allocate when aggregation will be over.
-        */
-       sw_rx_data_prod->mapping = replace_buf->mapping;
-
-       sw_rx_data_prod->data = replace_buf->data;
-       rx_bd_prod->addr.hi = cpu_to_le32(upper_32_bits(mapping));
-       rx_bd_prod->addr.lo = cpu_to_le32(lower_32_bits(mapping));
-       sw_rx_data_prod->page_offset = replace_buf->page_offset;
-
-       rxq->sw_rx_prod++;
+       tpa_info->skb = qede_tpa_rx_build_skb(edev, rxq, sw_rx_data_cons,
+                                             le16_to_cpu(cqe->len_on_first_bd),
+                                             pad, false);
+       tpa_info->buffer.page_offset = sw_rx_data_cons->page_offset;
+       tpa_info->buffer.mapping = sw_rx_data_cons->mapping;
 
-       /* move partial skb from cons to pool (don't unmap yet)
-        * save mapping, incase we drop the packet later on.
-        */
-       tpa_info->buffer = *sw_rx_data_cons;
-       mapping = HILO_U64(le32_to_cpu(rx_bd_cons->addr.hi),
-                          le32_to_cpu(rx_bd_cons->addr.lo));
-
-       tpa_info->buffer_mapping = mapping;
-       rxq->sw_rx_cons++;
-
-       /* set tpa state to start only if we are able to allocate skb
-        * for this aggregation, otherwise mark as error and aggregation will
-        * be dropped
-        */
-       tpa_info->skb = netdev_alloc_skb(edev->ndev,
-                                        le16_to_cpu(cqe->len_on_first_bd));
        if (unlikely(!tpa_info->skb)) {
                DP_NOTICE(edev, "Failed to allocate SKB for gro\n");
+
+               /* Consume from ring but do not produce since
+                * this might be used by FW still, it will be re-used
+                * at TPA end.
+                */
+               tpa_info->tpa_start_fail = true;
+               qede_rx_bd_ring_consume(rxq);
                tpa_info->state = QEDE_AGG_STATE_ERROR;
                goto cons_buf;
        }
 
-       /* Start filling in the aggregation info */
-       skb_put(tpa_info->skb, le16_to_cpu(cqe->len_on_first_bd));
        tpa_info->frag_id = 0;
        tpa_info->state = QEDE_AGG_STATE_START;
 
-       /* Store some information from first CQE */
-       tpa_info->start_cqe_placement_offset = cqe->placement_offset;
-       tpa_info->start_cqe_bd_len = le16_to_cpu(cqe->len_on_first_bd);
        if ((le16_to_cpu(cqe->pars_flags.flags) >>
             PARSING_AND_ERR_FLAGS_TAG8021QEXIST_SHIFT) &
            PARSING_AND_ERR_FLAGS_TAG8021QEXIST_MASK)
@@ -899,6 +964,10 @@ static int qede_tpa_end(struct qede_dev *edev,
        tpa_info = &rxq->tpa_info[cqe->tpa_agg_index];
        skb = tpa_info->skb;
 
+       if (tpa_info->buffer.page_offset == PAGE_SIZE)
+               dma_unmap_page(rxq->dev, tpa_info->buffer.mapping,
+                              PAGE_SIZE, rxq->data_direction);
+
        for (i = 0; cqe->len_list[i]; i++)
                qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index,
                                   le16_to_cpu(cqe->len_list[i]));
@@ -919,11 +988,6 @@ static int qede_tpa_end(struct qede_dev *edev,
                       "Strange - total packet len [cqe] is %4x but SKB has len %04x\n",
                       le16_to_cpu(cqe->total_packet_len), skb->len);
 
-       memcpy(skb->data,
-              page_address(tpa_info->buffer.data) +
-              tpa_info->start_cqe_placement_offset +
-              tpa_info->buffer.page_offset, tpa_info->start_cqe_bd_len);
-
        /* Finalize the SKB */
        skb->protocol = eth_type_trans(skb, edev->ndev);
        skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -940,6 +1004,12 @@ static int qede_tpa_end(struct qede_dev *edev,
        return 1;
 err:
        tpa_info->state = QEDE_AGG_STATE_NONE;
+
+       if (tpa_info->tpa_start_fail) {
+               qede_reuse_page(rxq, &tpa_info->buffer);
+               tpa_info->tpa_start_fail = false;
+       }
+
        dev_kfree_skb_any(tpa_info->skb);
        tpa_info->skb = NULL;
        return 0;
@@ -1058,65 +1128,6 @@ static bool qede_rx_xdp(struct qede_dev *edev,
        return false;
 }
 
-static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev,
-                                           struct qede_rx_queue *rxq,
-                                           struct sw_rx_data *bd, u16 len,
-                                           u16 pad)
-{
-       unsigned int offset = bd->page_offset + pad;
-       struct skb_frag_struct *frag;
-       struct page *page = bd->data;
-       unsigned int pull_len;
-       struct sk_buff *skb;
-       unsigned char *va;
-
-       /* Allocate a new SKB with a sufficient large header len */
-       skb = netdev_alloc_skb(edev->ndev, QEDE_RX_HDR_SIZE);
-       if (unlikely(!skb))
-               return NULL;
-
-       /* Copy data into SKB - if it's small, we can simply copy it and
-        * re-use the already allcoated & mapped memory.
-        */
-       if (len + pad <= edev->rx_copybreak) {
-               skb_put_data(skb, page_address(page) + offset, len);
-               qede_reuse_page(rxq, bd);
-               goto out;
-       }
-
-       frag = &skb_shinfo(skb)->frags[0];
-
-       skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
-                       page, offset, len, rxq->rx_buf_seg_size);
-
-       va = skb_frag_address(frag);
-       pull_len = eth_get_headlen(va, QEDE_RX_HDR_SIZE);
-
-       /* Align the pull_len to optimize memcpy */
-       memcpy(skb->data, va, ALIGN(pull_len, sizeof(long)));
-
-       /* Correct the skb & frag sizes offset after the pull */
-       skb_frag_size_sub(frag, pull_len);
-       frag->page_offset += pull_len;
-       skb->data_len -= pull_len;
-       skb->tail += pull_len;
-
-       if (unlikely(qede_realloc_rx_buffer(rxq, bd))) {
-               /* Incr page ref count to reuse on allocation failure so
-                * that it doesn't get freed while freeing SKB [as its
-                * already mapped there].
-                */
-               page_ref_inc(page);
-               dev_kfree_skb_any(skb);
-               return NULL;
-       }
-
-out:
-       /* We've consumed the first BD and prepared an SKB */
-       qede_rx_bd_ring_consume(rxq);
-       return skb;
-}
-
 static int qede_rx_build_jumbo(struct qede_dev *edev,
                               struct qede_rx_queue *rxq,
                               struct sk_buff *skb,
@@ -1157,7 +1168,7 @@ static int qede_rx_build_jumbo(struct qede_dev *edev,
                               PAGE_SIZE, DMA_FROM_DEVICE);
 
                skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags++,
-                                  bd->data, 0, cur_size);
+                                  bd->data, rxq->rx_headroom, cur_size);
 
                skb->truesize += PAGE_SIZE;
                skb->data_len += cur_size;
@@ -1256,7 +1267,7 @@ static int qede_rx_process_cqe(struct qede_dev *edev,
        /* Basic validation passed; Need to prepare an SKB. This would also
         * guarantee to finally consume the first BD upon success.
         */
-       skb = qede_rx_allocate_skb(edev, rxq, bd, len, pad);
+       skb = qede_rx_build_skb(edev, rxq, bd, len, pad);
        if (!skb) {
                rxq->rx_alloc_errors++;
                qede_recycle_rx_bd_ring(rxq, fp_cqe->bd_num);
index 89c581c..9e70f71 100644 (file)
@@ -133,6 +133,9 @@ static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id);
 static void qede_remove(struct pci_dev *pdev);
 static void qede_shutdown(struct pci_dev *pdev);
 static void qede_link_update(void *dev, struct qed_link_output *link);
+static void qede_get_eth_tlv_data(void *edev, void *data);
+static void qede_get_generic_tlv_data(void *edev,
+                                     struct qed_generic_tlvs *data);
 
 /* The qede lock is used to protect driver state change and driver flows that
  * are not reentrant.
@@ -228,6 +231,8 @@ static struct qed_eth_cb_ops qede_ll_ops = {
                .arfs_filter_op = qede_arfs_filter_op,
 #endif
                .link_update = qede_link_update,
+               .get_generic_tlv_data = qede_get_generic_tlv_data,
+               .get_protocol_tlv_data = qede_get_eth_tlv_data,
        },
        .force_mac = qede_force_mac,
        .ports_update = qede_udp_ports_update,
@@ -1066,13 +1071,12 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode)
 
        DP_INFO(edev, "Starting qede_remove\n");
 
+       qede_rdma_dev_remove(edev);
        unregister_netdev(ndev);
        cancel_delayed_work_sync(&edev->sp_task);
 
        qede_ptp_disable(edev);
 
-       qede_rdma_dev_remove(edev);
-
        edev->ops->common->set_power_state(cdev, PCI_D0);
 
        pci_set_drvdata(pdev, NULL);
@@ -1197,30 +1201,8 @@ static void qede_free_rx_buffers(struct qede_dev *edev,
        }
 }
 
-static void qede_free_sge_mem(struct qede_dev *edev, struct qede_rx_queue *rxq)
-{
-       int i;
-
-       if (edev->gro_disable)
-               return;
-
-       for (i = 0; i < ETH_TPA_MAX_AGGS_NUM; i++) {
-               struct qede_agg_info *tpa_info = &rxq->tpa_info[i];
-               struct sw_rx_data *replace_buf = &tpa_info->buffer;
-
-               if (replace_buf->data) {
-                       dma_unmap_page(&edev->pdev->dev,
-                                      replace_buf->mapping,
-                                      PAGE_SIZE, DMA_FROM_DEVICE);
-                       __free_page(replace_buf->data);
-               }
-       }
-}
-
 static void qede_free_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq)
 {
-       qede_free_sge_mem(edev, rxq);
-
        /* Free rx buffers */
        qede_free_rx_buffers(edev, rxq);
 
@@ -1232,45 +1214,15 @@ static void qede_free_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq)
        edev->ops->common->chain_free(edev->cdev, &rxq->rx_comp_ring);
 }
 
-static int qede_alloc_sge_mem(struct qede_dev *edev, struct qede_rx_queue *rxq)
+static void qede_set_tpa_param(struct qede_rx_queue *rxq)
 {
-       dma_addr_t mapping;
        int i;
 
-       if (edev->gro_disable)
-               return 0;
-
        for (i = 0; i < ETH_TPA_MAX_AGGS_NUM; i++) {
                struct qede_agg_info *tpa_info = &rxq->tpa_info[i];
-               struct sw_rx_data *replace_buf = &tpa_info->buffer;
 
-               replace_buf->data = alloc_pages(GFP_ATOMIC, 0);
-               if (unlikely(!replace_buf->data)) {
-                       DP_NOTICE(edev,
-                                 "Failed to allocate TPA skb pool [replacement buffer]\n");
-                       goto err;
-               }
-
-               mapping = dma_map_page(&edev->pdev->dev, replace_buf->data, 0,
-                                      PAGE_SIZE, DMA_FROM_DEVICE);
-               if (unlikely(dma_mapping_error(&edev->pdev->dev, mapping))) {
-                       DP_NOTICE(edev,
-                                 "Failed to map TPA replacement buffer\n");
-                       goto err;
-               }
-
-               replace_buf->mapping = mapping;
-               tpa_info->buffer.page_offset = 0;
-               tpa_info->buffer_mapping = mapping;
                tpa_info->state = QEDE_AGG_STATE_NONE;
        }
-
-       return 0;
-err:
-       qede_free_sge_mem(edev, rxq);
-       edev->gro_disable = 1;
-       edev->ndev->features &= ~NETIF_F_GRO_HW;
-       return -ENOMEM;
 }
 
 /* This function allocates all memory needed per Rx queue */
@@ -1281,19 +1233,24 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq)
        rxq->num_rx_buffers = edev->q_num_rx_buffers;
 
        rxq->rx_buf_size = NET_IP_ALIGN + ETH_OVERHEAD + edev->ndev->mtu;
-       rxq->rx_headroom = edev->xdp_prog ? XDP_PACKET_HEADROOM : 0;
+
+       rxq->rx_headroom = edev->xdp_prog ? XDP_PACKET_HEADROOM : NET_SKB_PAD;
+       size = rxq->rx_headroom +
+              SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 
        /* Make sure that the headroom and  payload fit in a single page */
-       if (rxq->rx_buf_size + rxq->rx_headroom > PAGE_SIZE)
-               rxq->rx_buf_size = PAGE_SIZE - rxq->rx_headroom;
+       if (rxq->rx_buf_size + size > PAGE_SIZE)
+               rxq->rx_buf_size = PAGE_SIZE - size;
 
-       /* Segment size to spilt a page in multiple equal parts,
+       /* Segment size to spilt a page in multiple equal parts ,
         * unless XDP is used in which case we'd use the entire page.
         */
-       if (!edev->xdp_prog)
-               rxq->rx_buf_seg_size = roundup_pow_of_two(rxq->rx_buf_size);
-       else
+       if (!edev->xdp_prog) {
+               size = size + rxq->rx_buf_size;
+               rxq->rx_buf_seg_size = roundup_pow_of_two(size);
+       } else {
                rxq->rx_buf_seg_size = PAGE_SIZE;
+       }
 
        /* Allocate the parallel driver ring for Rx buffers */
        size = sizeof(*rxq->sw_rx_ring) * RX_RING_SIZE;
@@ -1337,7 +1294,8 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq)
                }
        }
 
-       rc = qede_alloc_sge_mem(edev, rxq);
+       if (!edev->gro_disable)
+               qede_set_tpa_param(rxq);
 err:
        return rc;
 }
@@ -2178,3 +2136,99 @@ static void qede_link_update(void *dev, struct qed_link_output *link)
                }
        }
 }
+
+static bool qede_is_txq_full(struct qede_dev *edev, struct qede_tx_queue *txq)
+{
+       struct netdev_queue *netdev_txq;
+
+       netdev_txq = netdev_get_tx_queue(edev->ndev, txq->index);
+       if (netif_xmit_stopped(netdev_txq))
+               return true;
+
+       return false;
+}
+
+static void qede_get_generic_tlv_data(void *dev, struct qed_generic_tlvs *data)
+{
+       struct qede_dev *edev = dev;
+       struct netdev_hw_addr *ha;
+       int i;
+
+       if (edev->ndev->features & NETIF_F_IP_CSUM)
+               data->feat_flags |= QED_TLV_IP_CSUM;
+       if (edev->ndev->features & NETIF_F_TSO)
+               data->feat_flags |= QED_TLV_LSO;
+
+       ether_addr_copy(data->mac[0], edev->ndev->dev_addr);
+       memset(data->mac[1], 0, ETH_ALEN);
+       memset(data->mac[2], 0, ETH_ALEN);
+       /* Copy the first two UC macs */
+       netif_addr_lock_bh(edev->ndev);
+       i = 1;
+       netdev_for_each_uc_addr(ha, edev->ndev) {
+               ether_addr_copy(data->mac[i++], ha->addr);
+               if (i == QED_TLV_MAC_COUNT)
+                       break;
+       }
+
+       netif_addr_unlock_bh(edev->ndev);
+}
+
+static void qede_get_eth_tlv_data(void *dev, void *data)
+{
+       struct qed_mfw_tlv_eth *etlv = data;
+       struct qede_dev *edev = dev;
+       struct qede_fastpath *fp;
+       int i;
+
+       etlv->lso_maxoff_size = 0XFFFF;
+       etlv->lso_maxoff_size_set = true;
+       etlv->lso_minseg_size = (u16)ETH_TX_LSO_WINDOW_MIN_LEN;
+       etlv->lso_minseg_size_set = true;
+       etlv->prom_mode = !!(edev->ndev->flags & IFF_PROMISC);
+       etlv->prom_mode_set = true;
+       etlv->tx_descr_size = QEDE_TSS_COUNT(edev);
+       etlv->tx_descr_size_set = true;
+       etlv->rx_descr_size = QEDE_RSS_COUNT(edev);
+       etlv->rx_descr_size_set = true;
+       etlv->iov_offload = QED_MFW_TLV_IOV_OFFLOAD_VEB;
+       etlv->iov_offload_set = true;
+
+       /* Fill information regarding queues; Should be done under the qede
+        * lock to guarantee those don't change beneath our feet.
+        */
+       etlv->txqs_empty = true;
+       etlv->rxqs_empty = true;
+       etlv->num_txqs_full = 0;
+       etlv->num_rxqs_full = 0;
+
+       __qede_lock(edev);
+       for_each_queue(i) {
+               fp = &edev->fp_array[i];
+               if (fp->type & QEDE_FASTPATH_TX) {
+                       if (fp->txq->sw_tx_cons != fp->txq->sw_tx_prod)
+                               etlv->txqs_empty = false;
+                       if (qede_is_txq_full(edev, fp->txq))
+                               etlv->num_txqs_full++;
+               }
+               if (fp->type & QEDE_FASTPATH_RX) {
+                       if (qede_has_rx_work(fp->rxq))
+                               etlv->rxqs_empty = false;
+
+                       /* This one is a bit tricky; Firmware might stop
+                        * placing packets if ring is not yet full.
+                        * Give an approximation.
+                        */
+                       if (le16_to_cpu(*fp->rxq->hw_cons_ptr) -
+                           qed_chain_get_cons_idx(&fp->rxq->rx_comp_ring) >
+                           RX_RING_SIZE - 100)
+                               etlv->num_rxqs_full++;
+               }
+       }
+       __qede_unlock(edev);
+
+       etlv->txqs_empty_set = true;
+       etlv->rxqs_empty_set = true;
+       etlv->num_txqs_full_set = true;
+       etlv->num_rxqs_full_set = true;
+}
index d5a32b7..031f6e6 100644 (file)
@@ -683,10 +683,11 @@ static int emac_tx_q_desc_alloc(struct emac_adapter *adpt,
                                struct emac_tx_queue *tx_q)
 {
        struct emac_ring_header *ring_header = &adpt->ring_header;
+       int node = dev_to_node(adpt->netdev->dev.parent);
        size_t size;
 
        size = sizeof(struct emac_buffer) * tx_q->tpd.count;
-       tx_q->tpd.tpbuff = kzalloc(size, GFP_KERNEL);
+       tx_q->tpd.tpbuff = kzalloc_node(size, GFP_KERNEL, node);
        if (!tx_q->tpd.tpbuff)
                return -ENOMEM;
 
@@ -723,11 +724,12 @@ static void emac_rx_q_bufs_free(struct emac_adapter *adpt)
 static int emac_rx_descs_alloc(struct emac_adapter *adpt)
 {
        struct emac_ring_header *ring_header = &adpt->ring_header;
+       int node = dev_to_node(adpt->netdev->dev.parent);
        struct emac_rx_queue *rx_q = &adpt->rx_q;
        size_t size;
 
        size = sizeof(struct emac_buffer) * rx_q->rfd.count;
-       rx_q->rfd.rfbuff = kzalloc(size, GFP_KERNEL);
+       rx_q->rfd.rfbuff = kzalloc_node(size, GFP_KERNEL, node);
        if (!rx_q->rfd.rfbuff)
                return -ENOMEM;
 
@@ -920,14 +922,13 @@ static void emac_mac_rx_descs_refill(struct emac_adapter *adpt,
 static void emac_adjust_link(struct net_device *netdev)
 {
        struct emac_adapter *adpt = netdev_priv(netdev);
-       struct emac_sgmii *sgmii = &adpt->phy;
        struct phy_device *phydev = netdev->phydev;
 
        if (phydev->link) {
                emac_mac_start(adpt);
-               sgmii->link_up(adpt);
+               emac_sgmii_link_change(adpt, true);
        } else {
-               sgmii->link_down(adpt);
+               emac_sgmii_link_change(adpt, false);
                emac_mac_stop(adpt);
        }
 
index e8ab512..562420b 100644 (file)
 
 #define SERDES_START_WAIT_TIMES                        100
 
+int emac_sgmii_init(struct emac_adapter *adpt)
+{
+       if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->init))
+               return 0;
+
+       return adpt->phy.sgmii_ops->init(adpt);
+}
+
+int emac_sgmii_open(struct emac_adapter *adpt)
+{
+       if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->open))
+               return 0;
+
+       return adpt->phy.sgmii_ops->open(adpt);
+}
+
+void emac_sgmii_close(struct emac_adapter *adpt)
+{
+       if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->close))
+               return;
+
+       adpt->phy.sgmii_ops->close(adpt);
+}
+
+int emac_sgmii_link_change(struct emac_adapter *adpt, bool link_state)
+{
+       if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->link_change))
+               return 0;
+
+       return adpt->phy.sgmii_ops->link_change(adpt, link_state);
+}
+
+void emac_sgmii_reset(struct emac_adapter *adpt)
+{
+       if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->reset))
+               return;
+
+       adpt->phy.sgmii_ops->reset(adpt);
+}
+
 /* Initialize the SGMII link between the internal and external PHYs. */
 static void emac_sgmii_link_init(struct emac_adapter *adpt)
 {
@@ -163,21 +203,21 @@ static void emac_sgmii_reset_prepare(struct emac_adapter *adpt)
        msleep(50);
 }
 
-void emac_sgmii_reset(struct emac_adapter *adpt)
+static void emac_sgmii_common_reset(struct emac_adapter *adpt)
 {
        int ret;
 
        emac_sgmii_reset_prepare(adpt);
        emac_sgmii_link_init(adpt);
 
-       ret = adpt->phy.initialize(adpt);
+       ret = emac_sgmii_init(adpt);
        if (ret)
                netdev_err(adpt->netdev,
                           "could not reinitialize internal PHY (error=%i)\n",
                           ret);
 }
 
-static int emac_sgmii_open(struct emac_adapter *adpt)
+static int emac_sgmii_common_open(struct emac_adapter *adpt)
 {
        struct emac_sgmii *sgmii = &adpt->phy;
        int ret;
@@ -201,43 +241,53 @@ static int emac_sgmii_open(struct emac_adapter *adpt)
        return 0;
 }
 
-static int emac_sgmii_close(struct emac_adapter *adpt)
+static void emac_sgmii_common_close(struct emac_adapter *adpt)
 {
        struct emac_sgmii *sgmii = &adpt->phy;
 
        /* Make sure interrupts are disabled */
        writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
        free_irq(sgmii->irq, adpt);
-
-       return 0;
 }
 
 /* The error interrupts are only valid after the link is up */
-static int emac_sgmii_link_up(struct emac_adapter *adpt)
+static int emac_sgmii_common_link_change(struct emac_adapter *adpt, bool linkup)
 {
        struct emac_sgmii *sgmii = &adpt->phy;
        int ret;
 
-       /* Clear and enable interrupts */
-       ret = emac_sgmii_irq_clear(adpt, 0xff);
-       if (ret)
-               return ret;
+       if (linkup) {
+               /* Clear and enable interrupts */
+               ret = emac_sgmii_irq_clear(adpt, 0xff);
+               if (ret)
+                       return ret;
 
-       writel(SGMII_ISR_MASK, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
+               writel(SGMII_ISR_MASK,
+                      sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
+       } else {
+               /* Disable interrupts */
+               writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
+               synchronize_irq(sgmii->irq);
+       }
 
        return 0;
 }
 
-static int emac_sgmii_link_down(struct emac_adapter *adpt)
-{
-       struct emac_sgmii *sgmii = &adpt->phy;
-
-       /* Disable interrupts */
-       writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
-       synchronize_irq(sgmii->irq);
+static struct sgmii_ops qdf2432_ops = {
+       .init = emac_sgmii_init_qdf2432,
+       .open = emac_sgmii_common_open,
+       .close = emac_sgmii_common_close,
+       .link_change = emac_sgmii_common_link_change,
+       .reset = emac_sgmii_common_reset,
+};
 
-       return 0;
-}
+static struct sgmii_ops qdf2400_ops = {
+       .init = emac_sgmii_init_qdf2400,
+       .open = emac_sgmii_common_open,
+       .close = emac_sgmii_common_close,
+       .link_change = emac_sgmii_common_link_change,
+       .reset = emac_sgmii_common_reset,
+};
 
 static int emac_sgmii_acpi_match(struct device *dev, void *data)
 {
@@ -249,7 +299,7 @@ static int emac_sgmii_acpi_match(struct device *dev, void *data)
                {}
        };
        const struct acpi_device_id *id = acpi_match_device(match_table, dev);
-       emac_sgmii_function *initialize = data;
+       struct sgmii_ops **ops = data;
 
        if (id) {
                acpi_handle handle = ACPI_HANDLE(dev);
@@ -270,10 +320,10 @@ static int emac_sgmii_acpi_match(struct device *dev, void *data)
 
                switch (hrv) {
                case 1:
-                       *initialize = emac_sgmii_init_qdf2432;
+                       *ops = &qdf2432_ops;
                        return 1;
                case 2:
-                       *initialize = emac_sgmii_init_qdf2400;
+                       *ops = &qdf2400_ops;
                        return 1;
                }
        }
@@ -294,14 +344,6 @@ static const struct of_device_id emac_sgmii_dt_match[] = {
        {}
 };
 
-/* Dummy function for systems without an internal PHY. This avoids having
- * to check for NULL pointers before calling the functions.
- */
-static int emac_sgmii_dummy(struct emac_adapter *adpt)
-{
-       return 0;
-}
-
 int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt)
 {
        struct platform_device *sgmii_pdev = NULL;
@@ -312,22 +354,11 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt)
        if (has_acpi_companion(&pdev->dev)) {
                struct device *dev;
 
-               dev = device_find_child(&pdev->dev, &phy->initialize,
+               dev = device_find_child(&pdev->dev, &phy->sgmii_ops,
                                        emac_sgmii_acpi_match);
 
                if (!dev) {
                        dev_warn(&pdev->dev, "cannot find internal phy node\n");
-                       /* There is typically no internal PHY on emulation
-                        * systems, so if we can't find the node, assume
-                        * we are on an emulation system and stub-out
-                        * support for the internal PHY.  These systems only
-                        * use ACPI.
-                        */
-                       phy->open = emac_sgmii_dummy;
-                       phy->close = emac_sgmii_dummy;
-                       phy->link_up = emac_sgmii_dummy;
-                       phy->link_down = emac_sgmii_dummy;
-
                        return 0;
                }
 
@@ -355,14 +386,9 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt)
                        goto error_put_device;
                }
 
-               phy->initialize = (emac_sgmii_function)match->data;
+               phy->sgmii_ops->init = match->data;
        }
 
-       phy->open = emac_sgmii_open;
-       phy->close = emac_sgmii_close;
-       phy->link_up = emac_sgmii_link_up;
-       phy->link_down = emac_sgmii_link_down;
-
        /* Base address is the first address */
        res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0);
        if (!res) {
@@ -386,7 +412,7 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt)
                }
        }
 
-       ret = phy->initialize(adpt);
+       ret = emac_sgmii_init(adpt);
        if (ret)
                goto error;
 
index e7c0c3b..31ba21e 100644 (file)
 struct emac_adapter;
 struct platform_device;
 
-typedef int (*emac_sgmii_function)(struct emac_adapter *adpt);
+/** emac_sgmii - internal emac phy
+ * @init initialization function
+ * @open called when the driver is opened
+ * @close called when the driver is closed
+ * @link_change called when the link state changes
+ */
+struct sgmii_ops {
+       int (*init)(struct emac_adapter *adpt);
+       int (*open)(struct emac_adapter *adpt);
+       void (*close)(struct emac_adapter *adpt);
+       int (*link_change)(struct emac_adapter *adpt, bool link_state);
+       void (*reset)(struct emac_adapter *adpt);
+};
 
 /** emac_sgmii - internal emac phy
  * @base base address
  * @digital per-lane digital block
  * @irq the interrupt number
  * @decode_error_count reference count of consecutive decode errors
- * @initialize initialization function
- * @open called when the driver is opened
- * @close called when the driver is closed
- * @link_up called when the link comes up
- * @link_down called when the link comes down
+ * @sgmii_ops sgmii ops
  */
 struct emac_sgmii {
        void __iomem            *base;
        void __iomem            *digital;
        unsigned int            irq;
        atomic_t                decode_error_count;
-       emac_sgmii_function     initialize;
-       emac_sgmii_function     open;
-       emac_sgmii_function     close;
-       emac_sgmii_function     link_up;
-       emac_sgmii_function     link_down;
+       struct  sgmii_ops       *sgmii_ops;
 };
 
 int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt);
-void emac_sgmii_reset(struct emac_adapter *adpt);
 
 int emac_sgmii_init_fsm9900(struct emac_adapter *adpt);
 int emac_sgmii_init_qdf2432(struct emac_adapter *adpt);
 int emac_sgmii_init_qdf2400(struct emac_adapter *adpt);
 
+int emac_sgmii_init(struct emac_adapter *adpt);
+int emac_sgmii_open(struct emac_adapter *adpt);
+void emac_sgmii_close(struct emac_adapter *adpt);
+int emac_sgmii_link_change(struct emac_adapter *adpt, bool link_state);
+void emac_sgmii_reset(struct emac_adapter *adpt);
 #endif
index 13235ba..2a0cbc5 100644 (file)
@@ -253,7 +253,7 @@ static int emac_open(struct net_device *netdev)
                return ret;
        }
 
-       ret = adpt->phy.open(adpt);
+       ret = emac_sgmii_open(adpt);
        if (ret) {
                emac_mac_rx_tx_rings_free_all(adpt);
                free_irq(irq->irq, irq);
@@ -264,7 +264,7 @@ static int emac_open(struct net_device *netdev)
        if (ret) {
                emac_mac_rx_tx_rings_free_all(adpt);
                free_irq(irq->irq, irq);
-               adpt->phy.close(adpt);
+               emac_sgmii_close(adpt);
                return ret;
        }
 
@@ -278,7 +278,7 @@ static int emac_close(struct net_device *netdev)
 
        mutex_lock(&adpt->reset_lock);
 
-       adpt->phy.close(adpt);
+       emac_sgmii_close(adpt);
        emac_mac_down(adpt);
        emac_mac_rx_tx_rings_free_all(adpt);
 
@@ -761,11 +761,10 @@ static void emac_shutdown(struct platform_device *pdev)
 {
        struct net_device *netdev = dev_get_drvdata(&pdev->dev);
        struct emac_adapter *adpt = netdev_priv(netdev);
-       struct emac_sgmii *sgmii = &adpt->phy;
 
        if (netdev->flags & IFF_UP) {
                /* Closing the SGMII turns off its interrupts */
-               sgmii->close(adpt);
+               emac_sgmii_close(adpt);
 
                /* Resetting the MAC turns off all DMA and its interrupts */
                emac_mac_reset(adpt);
index 0b5b5da..34ac45a 100644 (file)
@@ -54,11 +54,24 @@ struct rmnet_pcpu_stats {
        struct u64_stats_sync syncp;
 };
 
+struct rmnet_priv_stats {
+       u64 csum_ok;
+       u64 csum_valid_unset;
+       u64 csum_validation_failed;
+       u64 csum_err_bad_buffer;
+       u64 csum_err_invalid_ip_version;
+       u64 csum_err_invalid_transport;
+       u64 csum_fragmented_pkt;
+       u64 csum_skipped;
+       u64 csum_sw;
+};
+
 struct rmnet_priv {
        u8 mux_id;
        struct net_device *real_dev;
        struct rmnet_pcpu_stats __percpu *pcpu_stats;
        struct gro_cells gro_cells;
+       struct rmnet_priv_stats stats;
 };
 
 struct rmnet_port *rmnet_get_port(struct net_device *real_dev);
index 6fcd586..7fd86d4 100644 (file)
@@ -148,7 +148,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb,
 
        if (skb_headroom(skb) < required_headroom) {
                if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL))
-                       goto fail;
+                       return -ENOMEM;
        }
 
        if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4)
@@ -156,17 +156,13 @@ static int rmnet_map_egress_handler(struct sk_buff *skb,
 
        map_header = rmnet_map_add_map_header(skb, additional_header_len, 0);
        if (!map_header)
-               goto fail;
+               return -ENOMEM;
 
        map_header->mux_id = mux_id;
 
        skb->protocol = htons(ETH_P_MAP);
 
        return 0;
-
-fail:
-       kfree_skb(skb);
-       return -ENOMEM;
 }
 
 static void
@@ -228,15 +224,18 @@ void rmnet_egress_handler(struct sk_buff *skb)
        mux_id = priv->mux_id;
 
        port = rmnet_get_port(skb->dev);
-       if (!port) {
-               kfree_skb(skb);
-               return;
-       }
+       if (!port)
+               goto drop;
 
        if (rmnet_map_egress_handler(skb, port, mux_id, orig_dev))
-               return;
+               goto drop;
 
        rmnet_vnd_tx_fixup(skb, orig_dev);
 
        dev_queue_xmit(skb);
+       return;
+
+drop:
+       this_cpu_inc(priv->pcpu_stats->stats.tx_drops);
+       kfree_skb(skb);
 }
index 78fdad0..56a93df 100644 (file)
@@ -69,17 +69,9 @@ static void rmnet_map_send_ack(struct sk_buff *skb,
        struct rmnet_map_control_command *cmd;
        int xmit_status;
 
-       if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
-               if (skb->len < sizeof(struct rmnet_map_header) +
-                   RMNET_MAP_GET_LENGTH(skb) +
-                   sizeof(struct rmnet_map_dl_csum_trailer)) {
-                       kfree_skb(skb);
-                       return;
-               }
-
-               skb_trim(skb, skb->len -
-                        sizeof(struct rmnet_map_dl_csum_trailer));
-       }
+       if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4)
+               skb_trim(skb,
+                        skb->len - sizeof(struct rmnet_map_dl_csum_trailer));
 
        skb->protocol = htons(ETH_P_MAP);
 
index a6ea094..57a9c31 100644 (file)
@@ -48,7 +48,8 @@ static __sum16 *rmnet_map_get_csum_field(unsigned char protocol,
 
 static int
 rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
-                              struct rmnet_map_dl_csum_trailer *csum_trailer)
+                              struct rmnet_map_dl_csum_trailer *csum_trailer,
+                              struct rmnet_priv *priv)
 {
        __sum16 *csum_field, csum_temp, pseudo_csum, hdr_csum, ip_payload_csum;
        u16 csum_value, csum_value_final;
@@ -58,19 +59,25 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
 
        ip4h = (struct iphdr *)(skb->data);
        if ((ntohs(ip4h->frag_off) & IP_MF) ||
-           ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0))
+           ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0)) {
+               priv->stats.csum_fragmented_pkt++;
                return -EOPNOTSUPP;
+       }
 
        txporthdr = skb->data + ip4h->ihl * 4;
 
        csum_field = rmnet_map_get_csum_field(ip4h->protocol, txporthdr);
 
-       if (!csum_field)
+       if (!csum_field) {
+               priv->stats.csum_err_invalid_transport++;
                return -EPROTONOSUPPORT;
+       }
 
        /* RFC 768 - Skip IPv4 UDP packets where sender checksum field is 0 */
-       if (*csum_field == 0 && ip4h->protocol == IPPROTO_UDP)
+       if (*csum_field == 0 && ip4h->protocol == IPPROTO_UDP) {
+               priv->stats.csum_skipped++;
                return 0;
+       }
 
        csum_value = ~ntohs(csum_trailer->csum_value);
        hdr_csum = ~ip_fast_csum(ip4h, (int)ip4h->ihl);
@@ -102,16 +109,20 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
                }
        }
 
-       if (csum_value_final == ntohs((__force __be16)*csum_field))
+       if (csum_value_final == ntohs((__force __be16)*csum_field)) {
+               priv->stats.csum_ok++;
                return 0;
-       else
+       } else {
+               priv->stats.csum_validation_failed++;
                return -EINVAL;
+       }
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
 static int
 rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
-                              struct rmnet_map_dl_csum_trailer *csum_trailer)
+                              struct rmnet_map_dl_csum_trailer *csum_trailer,
+                              struct rmnet_priv *priv)
 {
        __sum16 *csum_field, ip6_payload_csum, pseudo_csum, csum_temp;
        u16 csum_value, csum_value_final;
@@ -125,8 +136,10 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
        txporthdr = skb->data + sizeof(struct ipv6hdr);
        csum_field = rmnet_map_get_csum_field(ip6h->nexthdr, txporthdr);
 
-       if (!csum_field)
+       if (!csum_field) {
+               priv->stats.csum_err_invalid_transport++;
                return -EPROTONOSUPPORT;
+       }
 
        csum_value = ~ntohs(csum_trailer->csum_value);
        ip6_hdr_csum = (__force __be16)
@@ -164,10 +177,13 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
                }
        }
 
-       if (csum_value_final == ntohs((__force __be16)*csum_field))
+       if (csum_value_final == ntohs((__force __be16)*csum_field)) {
+               priv->stats.csum_ok++;
                return 0;
-       else
+       } else {
+               priv->stats.csum_validation_failed++;
                return -EINVAL;
+       }
 }
 #endif
 
@@ -339,24 +355,34 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
  */
 int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len)
 {
+       struct rmnet_priv *priv = netdev_priv(skb->dev);
        struct rmnet_map_dl_csum_trailer *csum_trailer;
 
-       if (unlikely(!(skb->dev->features & NETIF_F_RXCSUM)))
+       if (unlikely(!(skb->dev->features & NETIF_F_RXCSUM))) {
+               priv->stats.csum_sw++;
                return -EOPNOTSUPP;
+       }
 
        csum_trailer = (struct rmnet_map_dl_csum_trailer *)(skb->data + len);
 
-       if (!csum_trailer->valid)
+       if (!csum_trailer->valid) {
+               priv->stats.csum_valid_unset++;
                return -EINVAL;
+       }
 
-       if (skb->protocol == htons(ETH_P_IP))
-               return rmnet_map_ipv4_dl_csum_trailer(skb, csum_trailer);
-       else if (skb->protocol == htons(ETH_P_IPV6))
+       if (skb->protocol == htons(ETH_P_IP)) {
+               return rmnet_map_ipv4_dl_csum_trailer(skb, csum_trailer, priv);
+       } else if (skb->protocol == htons(ETH_P_IPV6)) {
 #if IS_ENABLED(CONFIG_IPV6)
-               return rmnet_map_ipv6_dl_csum_trailer(skb, csum_trailer);
+               return rmnet_map_ipv6_dl_csum_trailer(skb, csum_trailer, priv);
 #else
+               priv->stats.csum_err_invalid_ip_version++;
                return -EPROTONOSUPPORT;
 #endif
+       } else {
+               priv->stats.csum_err_invalid_ip_version++;
+               return -EPROTONOSUPPORT;
+       }
 
        return 0;
 }
@@ -367,6 +393,7 @@ int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len)
 void rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
                                      struct net_device *orig_dev)
 {
+       struct rmnet_priv *priv = netdev_priv(orig_dev);
        struct rmnet_map_ul_csum_header *ul_header;
        void *iphdr;
 
@@ -389,8 +416,11 @@ void rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
                        rmnet_map_ipv6_ul_csum_header(iphdr, ul_header, skb);
                        return;
 #else
+                       priv->stats.csum_err_invalid_ip_version++;
                        goto sw_csum;
 #endif
+               } else {
+                       priv->stats.csum_err_invalid_ip_version++;
                }
        }
 
@@ -399,4 +429,6 @@ sw_csum:
        ul_header->csum_insert_offset = 0;
        ul_header->csum_enabled = 0;
        ul_header->udp_ip4_ind = 0;
+
+       priv->stats.csum_sw++;
 }
index 2ea16a0..cb02e1a 100644 (file)
@@ -152,6 +152,56 @@ static const struct net_device_ops rmnet_vnd_ops = {
        .ndo_get_stats64 = rmnet_get_stats64,
 };
 
+static const char rmnet_gstrings_stats[][ETH_GSTRING_LEN] = {
+       "Checksum ok",
+       "Checksum valid bit not set",
+       "Checksum validation failed",
+       "Checksum error bad buffer",
+       "Checksum error bad ip version",
+       "Checksum error bad transport",
+       "Checksum skipped on ip fragment",
+       "Checksum skipped",
+       "Checksum computed in software",
+};
+
+static void rmnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
+{
+       switch (stringset) {
+       case ETH_SS_STATS:
+               memcpy(buf, &rmnet_gstrings_stats,
+                      sizeof(rmnet_gstrings_stats));
+               break;
+       }
+}
+
+static int rmnet_get_sset_count(struct net_device *dev, int sset)
+{
+       switch (sset) {
+       case ETH_SS_STATS:
+               return ARRAY_SIZE(rmnet_gstrings_stats);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void rmnet_get_ethtool_stats(struct net_device *dev,
+                                   struct ethtool_stats *stats, u64 *data)
+{
+       struct rmnet_priv *priv = netdev_priv(dev);
+       struct rmnet_priv_stats *st = &priv->stats;
+
+       if (!data)
+               return;
+
+       memcpy(data, st, ARRAY_SIZE(rmnet_gstrings_stats) * sizeof(u64));
+}
+
+static const struct ethtool_ops rmnet_ethtool_ops = {
+       .get_ethtool_stats = rmnet_get_ethtool_stats,
+       .get_strings = rmnet_get_strings,
+       .get_sset_count = rmnet_get_sset_count,
+};
+
 /* Called by kernel whenever a new rmnet<n> device is created. Sets MTU,
  * flags, ARP type, needed headroom, etc...
  */
@@ -170,6 +220,7 @@ void rmnet_vnd_setup(struct net_device *rmnet_dev)
        rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
 
        rmnet_dev->needs_free_netdev = true;
+       rmnet_dev->ethtool_ops = &rmnet_ethtool_ops;
 }
 
 /* Exposed API */
index 2c2f0c5..75dfac0 100644 (file)
@@ -5064,25 +5064,6 @@ static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
        RTL_W32(tp, RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_BIT_MASK(32));
 }
 
-static void rtl_hw_start(struct  rtl8169_private *tp)
-{
-       RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
-
-       tp->hw_start(tp);
-
-       rtl_set_rx_max_size(tp);
-       rtl_set_rx_tx_desc_registers(tp);
-       rtl_set_rx_tx_config_registers(tp);
-       RTL_W8(tp, Cfg9346, Cfg9346_Lock);
-
-       /* Initially a 10 us delay. Turned it into a PCI commit. - FR */
-       RTL_R8(tp, IntrMask);
-       RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb);
-       /* no early-rx interrupts */
-       RTL_W16(tp, MultiIntr, RTL_R16(tp, MultiIntr) & 0xf000);
-       rtl_irq_enable_all(tp);
-}
-
 static void rtl8169_set_magic_reg(struct rtl8169_private *tp, unsigned mac_version)
 {
        static const struct rtl_cfg2_info {
@@ -5160,6 +5141,26 @@ static void rtl_set_rx_mode(struct net_device *dev)
        RTL_W32(tp, RxConfig, tmp);
 }
 
+static void rtl_hw_start(struct  rtl8169_private *tp)
+{
+       RTL_W8(tp, Cfg9346, Cfg9346_Unlock);
+
+       tp->hw_start(tp);
+
+       rtl_set_rx_max_size(tp);
+       rtl_set_rx_tx_desc_registers(tp);
+       rtl_set_rx_tx_config_registers(tp);
+       RTL_W8(tp, Cfg9346, Cfg9346_Lock);
+
+       /* Initially a 10 us delay. Turned it into a PCI commit. - FR */
+       RTL_R8(tp, IntrMask);
+       RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb);
+       rtl_set_rx_mode(tp->dev);
+       /* no early-rx interrupts */
+       RTL_W16(tp, MultiIntr, RTL_R16(tp, MultiIntr) & 0xf000);
+       rtl_irq_enable_all(tp);
+}
+
 static void rtl_hw_start_8169(struct rtl8169_private *tp)
 {
        if (tp->mac_version == RTL_GIGA_MAC_VER_05)
index 5970d9e..d9cadfb 100644 (file)
@@ -466,6 +466,9 @@ static void sh_eth_select_mii(struct net_device *ndev)
        u32 value;
 
        switch (mdp->phy_interface) {
+       case PHY_INTERFACE_MODE_RGMII ... PHY_INTERFACE_MODE_RGMII_TXID:
+               value = 0x3;
+               break;
        case PHY_INTERFACE_MODE_GMII:
                value = 0x2;
                break;
@@ -703,7 +706,7 @@ static struct sh_eth_cpu_data rcar_gen1_data = {
                          EESIPR_RTLFIP | EESIPR_RTSFIP |
                          EESIPR_PREIP | EESIPR_CERFIP,
 
-       .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO,
+       .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_TRO,
        .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE |
                          EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE,
        .fdr_value      = 0x00000f0f,
@@ -735,7 +738,7 @@ static struct sh_eth_cpu_data rcar_gen2_data = {
                          EESIPR_RTLFIP | EESIPR_RTSFIP |
                          EESIPR_PREIP | EESIPR_CERFIP,
 
-       .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO,
+       .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_TRO,
        .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE |
                          EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE,
        .fdr_value      = 0x00000f0f,
@@ -750,6 +753,49 @@ static struct sh_eth_cpu_data rcar_gen2_data = {
        .rmiimode       = 1,
        .magic          = 1,
 };
+
+/* R8A77980 */
+static struct sh_eth_cpu_data r8a77980_data = {
+       .soft_reset     = sh_eth_soft_reset_gether,
+
+       .set_duplex     = sh_eth_set_duplex,
+       .set_rate       = sh_eth_set_rate_gether,
+
+       .register_type  = SH_ETH_REG_GIGABIT,
+
+       .edtrr_trns     = EDTRR_TRNS_GETHER,
+       .ecsr_value     = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD | ECSR_MPD,
+       .ecsipr_value   = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP |
+                         ECSIPR_MPDIP,
+       .eesipr_value   = EESIPR_RFCOFIP | EESIPR_ECIIP |
+                         EESIPR_FTCIP | EESIPR_TDEIP | EESIPR_TFUFIP |
+                         EESIPR_FRIP | EESIPR_RDEIP | EESIPR_RFOFIP |
+                         EESIPR_RMAFIP | EESIPR_RRFIP |
+                         EESIPR_RTLFIP | EESIPR_RTSFIP |
+                         EESIPR_PREIP | EESIPR_CERFIP,
+
+       .tx_check       = EESR_FTC | EESR_CD | EESR_TRO,
+       .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
+                         EESR_RFE | EESR_RDE | EESR_RFRMER |
+                         EESR_TFE | EESR_TDE | EESR_ECI,
+       .fdr_value      = 0x0000070f,
+
+       .apr            = 1,
+       .mpr            = 1,
+       .tpauser        = 1,
+       .bculr          = 1,
+       .hw_swap        = 1,
+       .nbst           = 1,
+       .rpadir         = 1,
+       .rpadir_value   = 2 << 16,
+       .no_trimd       = 1,
+       .no_ade         = 1,
+       .xdfar_rw       = 1,
+       .hw_checksum    = 1,
+       .select_mii     = 1,
+       .magic          = 1,
+       .cexcr          = 1,
+};
 #endif /* CONFIG_OF */
 
 static void sh_eth_set_rate_sh7724(struct net_device *ndev)
@@ -785,7 +831,7 @@ static struct sh_eth_cpu_data sh7724_data = {
                          EESIPR_RTLFIP | EESIPR_RTSFIP |
                          EESIPR_PREIP | EESIPR_CERFIP,
 
-       .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO,
+       .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_TRO,
        .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE |
                          EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE,
 
@@ -830,7 +876,7 @@ static struct sh_eth_cpu_data sh7757_data = {
                          EESIPR_RRFIP | EESIPR_RTLFIP | EESIPR_RTSFIP |
                          EESIPR_PREIP | EESIPR_CERFIP,
 
-       .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO,
+       .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_TRO,
        .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE |
                          EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE,
 
@@ -1431,8 +1477,13 @@ static int sh_eth_dev_init(struct net_device *ndev)
 
        sh_eth_write(ndev, mdp->cd->trscer_err_mask, TRSCER);
 
+       /* DMA transfer burst mode */
+       if (mdp->cd->nbst)
+               sh_eth_modify(ndev, EDMR, EDMR_NBST, EDMR_NBST);
+
+       /* Burst cycle count upper-limit */
        if (mdp->cd->bculr)
-               sh_eth_write(ndev, 0x800, BCULR);       /* Burst sycle set */
+               sh_eth_write(ndev, 0x800, BCULR);
 
        sh_eth_write(ndev, mdp->cd->fcftr_value, FCFTR);
 
@@ -3025,15 +3076,10 @@ static int sh_mdio_init(struct sh_eth_private *mdp,
                 pdev->name, pdev->id);
 
        /* register MDIO bus */
-       if (dev->of_node) {
-               ret = of_mdiobus_register(mdp->mii_bus, dev->of_node);
-       } else {
-               if (pd->phy_irq > 0)
-                       mdp->mii_bus->irq[pd->phy] = pd->phy_irq;
-
-               ret = mdiobus_register(mdp->mii_bus);
-       }
+       if (pd->phy_irq > 0)
+               mdp->mii_bus->irq[pd->phy] = pd->phy_irq;
 
+       ret = of_mdiobus_register(mdp->mii_bus, dev->of_node);
        if (ret)
                goto out_free_bus;
 
@@ -3132,6 +3178,7 @@ static const struct of_device_id sh_eth_match_table[] = {
        { .compatible = "renesas,ether-r8a7791", .data = &rcar_gen2_data },
        { .compatible = "renesas,ether-r8a7793", .data = &rcar_gen2_data },
        { .compatible = "renesas,ether-r8a7794", .data = &rcar_gen2_data },
+       { .compatible = "renesas,gether-r8a77980", .data = &r8a77980_data },
        { .compatible = "renesas,ether-r7s72100", .data = &r7s72100_data },
        { .compatible = "renesas,rcar-gen1-ether", .data = &rcar_gen1_data },
        { .compatible = "renesas,rcar-gen2-ether", .data = &rcar_gen2_data },
index a5b792c..5dee19b 100644 (file)
@@ -163,7 +163,7 @@ enum {
 };
 
 /* Driver's parameters */
-#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_RENESAS)
 #define SH_ETH_RX_ALIGN                32
 #else
 #define SH_ETH_RX_ALIGN                2
@@ -184,6 +184,7 @@ enum GECMR_BIT {
 
 /* EDMR */
 enum DMAC_M_BIT {
+       EDMR_NBST = 0x80,
        EDMR_EL = 0x40, /* Litte endian */
        EDMR_DL1 = 0x20, EDMR_DL0 = 0x10,
        EDMR_SRST_GETHER = 0x03,
@@ -242,7 +243,7 @@ enum EESR_BIT {
        EESR_CND        = 0x00000800,
        EESR_DLC        = 0x00000400,
        EESR_CD         = 0x00000200,
-       EESR_RTO        = 0x00000100,
+       EESR_TRO        = 0x00000100,
        EESR_RMAF       = 0x00000080,
        EESR_CEEF       = 0x00000040,
        EESR_CELF       = 0x00000020,
@@ -262,7 +263,7 @@ enum EESR_BIT {
                                 EESR_CERF)  /* Recv frame CRC error */
 
 #define DEFAULT_TX_CHECK       (EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | \
-                                EESR_RTO)
+                                EESR_TRO)
 #define DEFAULT_EESR_ERR_CHECK (EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | \
                                 EESR_RDE | EESR_RFRMER | EESR_ADE | \
                                 EESR_TFE | EESR_TDE)
@@ -498,20 +499,21 @@ struct sh_eth_cpu_data {
 
        /* hardware features */
        unsigned long irq_flags; /* IRQ configuration flags */
-       unsigned no_psr:1;      /* EtherC DO NOT have PSR */
-       unsigned apr:1;         /* EtherC have APR */
-       unsigned mpr:1;         /* EtherC have MPR */
-       unsigned tpauser:1;     /* EtherC have TPAUSER */
-       unsigned bculr:1;       /* EtherC have BCULR */
-       unsigned tsu:1;         /* EtherC have TSU */
-       unsigned hw_swap:1;     /* E-DMAC have DE bit in EDMR */
-       unsigned rpadir:1;      /* E-DMAC have RPADIR */
-       unsigned no_trimd:1;    /* E-DMAC DO NOT have TRIMD */
-       unsigned no_ade:1;      /* E-DMAC DO NOT have ADE bit in EESR */
+       unsigned no_psr:1;      /* EtherC DOES NOT have PSR */
+       unsigned apr:1;         /* EtherC has APR */
+       unsigned mpr:1;         /* EtherC has MPR */
+       unsigned tpauser:1;     /* EtherC has TPAUSER */
+       unsigned bculr:1;       /* EtherC has BCULR */
+       unsigned tsu:1;         /* EtherC has TSU */
+       unsigned hw_swap:1;     /* E-DMAC has DE bit in EDMR */
+       unsigned nbst:1;        /* E-DMAC has NBST bit in EDMR */
+       unsigned rpadir:1;      /* E-DMAC has RPADIR */
+       unsigned no_trimd:1;    /* E-DMAC DOES NOT have TRIMD */
+       unsigned no_ade:1;      /* E-DMAC DOES NOT have ADE bit in EESR */
        unsigned no_xdfar:1;    /* E-DMAC DOES NOT have RDFAR/TDFAR */
        unsigned xdfar_rw:1;    /* E-DMAC has writeable RDFAR/TDFAR */
        unsigned hw_checksum:1; /* E-DMAC has CSMR */
-       unsigned select_mii:1;  /* EtherC have RMII_MII (MII select register) */
+       unsigned select_mii:1;  /* EtherC has RMII_MII (MII select register) */
        unsigned rmiimode:1;    /* EtherC has RMIIMODE register */
        unsigned rtrate:1;      /* EtherC has RTRATE register */
        unsigned magic:1;       /* EtherC has ECMR.MPDE and ECSR.MPD */
index 13133b3..f08625a 100644 (file)
@@ -1104,30 +1104,20 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)
        } else {
                if (bsp_priv->clk_enabled) {
                        if (phy_iface == PHY_INTERFACE_MODE_RMII) {
-                               if (!IS_ERR(bsp_priv->mac_clk_rx))
-                                       clk_disable_unprepare(
-                                               bsp_priv->mac_clk_rx);
+                               clk_disable_unprepare(bsp_priv->mac_clk_rx);
 
-                               if (!IS_ERR(bsp_priv->clk_mac_ref))
-                                       clk_disable_unprepare(
-                                               bsp_priv->clk_mac_ref);
+                               clk_disable_unprepare(bsp_priv->clk_mac_ref);
 
-                               if (!IS_ERR(bsp_priv->clk_mac_refout))
-                                       clk_disable_unprepare(
-                                               bsp_priv->clk_mac_refout);
+                               clk_disable_unprepare(bsp_priv->clk_mac_refout);
                        }
 
-                       if (!IS_ERR(bsp_priv->clk_phy))
-                               clk_disable_unprepare(bsp_priv->clk_phy);
+                       clk_disable_unprepare(bsp_priv->clk_phy);
 
-                       if (!IS_ERR(bsp_priv->aclk_mac))
-                               clk_disable_unprepare(bsp_priv->aclk_mac);
+                       clk_disable_unprepare(bsp_priv->aclk_mac);
 
-                       if (!IS_ERR(bsp_priv->pclk_mac))
-                               clk_disable_unprepare(bsp_priv->pclk_mac);
+                       clk_disable_unprepare(bsp_priv->pclk_mac);
 
-                       if (!IS_ERR(bsp_priv->mac_clk_tx))
-                               clk_disable_unprepare(bsp_priv->mac_clk_tx);
+                       clk_disable_unprepare(bsp_priv->mac_clk_tx);
                        /**
                         * if (!IS_ERR(bsp_priv->clk_mac))
                         *      clk_disable_unprepare(bsp_priv->clk_mac);
index a3fa65b..2e6e2a9 100644 (file)
  *                             This value is used for disabling properly EMAC
  *                             and used as a good starting value in case of the
  *                             boot process(uboot) leave some stuff.
+ * @syscon_field               reg_field for the syscon's gmac register
  * @soc_has_internal_phy:      Does the MAC embed an internal PHY
  * @support_mii:               Does the MAC handle MII
  * @support_rmii:              Does the MAC handle RMII
  * @support_rgmii:             Does the MAC handle RGMII
+ *
+ * @rx_delay_max:              Maximum raw value for RX delay chain
+ * @tx_delay_max:              Maximum raw value for TX delay chain
+ *                             These two also indicate the bitmask for
+ *                             the RX and TX delay chain registers. A
+ *                             value of zero indicates this is not supported.
  */
 struct emac_variant {
        u32 default_syscon_value;
+       const struct reg_field *syscon_field;
        bool soc_has_internal_phy;
        bool support_mii;
        bool support_rmii;
        bool support_rgmii;
+       u8 rx_delay_max;
+       u8 tx_delay_max;
 };
 
 /* struct sunxi_priv_data - hold all sunxi private data
@@ -71,38 +81,70 @@ struct sunxi_priv_data {
        struct regulator *regulator;
        struct reset_control *rst_ephy;
        const struct emac_variant *variant;
-       struct regmap *regmap;
+       struct regmap_field *regmap_field;
        bool internal_phy_powered;
        void *mux_handle;
 };
 
+/* EMAC clock register @ 0x30 in the "system control" address range */
+static const struct reg_field sun8i_syscon_reg_field = {
+       .reg = 0x30,
+       .lsb = 0,
+       .msb = 31,
+};
+
+/* EMAC clock register @ 0x164 in the CCU address range */
+static const struct reg_field sun8i_ccu_reg_field = {
+       .reg = 0x164,
+       .lsb = 0,
+       .msb = 31,
+};
+
 static const struct emac_variant emac_variant_h3 = {
        .default_syscon_value = 0x58000,
+       .syscon_field = &sun8i_syscon_reg_field,
        .soc_has_internal_phy = true,
        .support_mii = true,
        .support_rmii = true,
-       .support_rgmii = true
+       .support_rgmii = true,
+       .rx_delay_max = 31,
+       .tx_delay_max = 7,
 };
 
 static const struct emac_variant emac_variant_v3s = {
        .default_syscon_value = 0x38000,
+       .syscon_field = &sun8i_syscon_reg_field,
        .soc_has_internal_phy = true,
        .support_mii = true
 };
 
 static const struct emac_variant emac_variant_a83t = {
        .default_syscon_value = 0,
+       .syscon_field = &sun8i_syscon_reg_field,
        .soc_has_internal_phy = false,
        .support_mii = true,
-       .support_rgmii = true
+       .support_rgmii = true,
+       .rx_delay_max = 31,
+       .tx_delay_max = 7,
+};
+
+static const struct emac_variant emac_variant_r40 = {
+       .default_syscon_value = 0,
+       .syscon_field = &sun8i_ccu_reg_field,
+       .support_mii = true,
+       .support_rgmii = true,
+       .rx_delay_max = 7,
 };
 
 static const struct emac_variant emac_variant_a64 = {
        .default_syscon_value = 0,
+       .syscon_field = &sun8i_syscon_reg_field,
        .soc_has_internal_phy = false,
        .support_mii = true,
        .support_rmii = true,
-       .support_rgmii = true
+       .support_rgmii = true,
+       .rx_delay_max = 31,
+       .tx_delay_max = 7,
 };
 
 #define EMAC_BASIC_CTL0 0x00
@@ -206,9 +248,7 @@ static const struct emac_variant emac_variant_a64 = {
 #define SYSCON_RMII_EN         BIT(13) /* 1: enable RMII (overrides EPIT) */
 
 /* Generic system control EMAC_CLK bits */
-#define SYSCON_ETXDC_MASK              GENMASK(2, 0)
 #define SYSCON_ETXDC_SHIFT             10
-#define SYSCON_ERXDC_MASK              GENMASK(4, 0)
 #define SYSCON_ERXDC_SHIFT             5
 /* EMAC PHY Interface Type */
 #define SYSCON_EPIT                    BIT(2) /* 1: RGMII, 0: MII */
@@ -216,7 +256,6 @@ static const struct emac_variant emac_variant_a64 = {
 #define SYSCON_ETCS_MII                0x0
 #define SYSCON_ETCS_EXT_GMII   0x1
 #define SYSCON_ETCS_INT_GMII   0x2
-#define SYSCON_EMAC_REG                0x30
 
 /* sun8i_dwmac_dma_reset() - reset the EMAC
  * Called from stmmac via stmmac_dma_ops->reset
@@ -237,17 +276,28 @@ static int sun8i_dwmac_dma_reset(void __iomem *ioaddr)
  * Called from stmmac via stmmac_dma_ops->init
  */
 static void sun8i_dwmac_dma_init(void __iomem *ioaddr,
-                                struct stmmac_dma_cfg *dma_cfg,
-                                u32 dma_tx, u32 dma_rx, int atds)
+                                struct stmmac_dma_cfg *dma_cfg, int atds)
 {
-       /* Write TX and RX descriptors address */
-       writel(dma_rx, ioaddr + EMAC_RX_DESC_LIST);
-       writel(dma_tx, ioaddr + EMAC_TX_DESC_LIST);
-
        writel(EMAC_RX_INT | EMAC_TX_INT, ioaddr + EMAC_INT_EN);
        writel(0x1FFFFFF, ioaddr + EMAC_INT_STA);
 }
 
+static void sun8i_dwmac_dma_init_rx(void __iomem *ioaddr,
+                                   struct stmmac_dma_cfg *dma_cfg,
+                                   u32 dma_rx_phy, u32 chan)
+{
+       /* Write RX descriptors address */
+       writel(dma_rx_phy, ioaddr + EMAC_RX_DESC_LIST);
+}
+
+static void sun8i_dwmac_dma_init_tx(void __iomem *ioaddr,
+                                   struct stmmac_dma_cfg *dma_cfg,
+                                   u32 dma_tx_phy, u32 chan)
+{
+       /* Write TX descriptors address */
+       writel(dma_tx_phy, ioaddr + EMAC_TX_DESC_LIST);
+}
+
 /* sun8i_dwmac_dump_regs() - Dump EMAC address space
  * Called from stmmac_dma_ops->dump_regs
  * Used for ethtool
@@ -398,13 +448,36 @@ static int sun8i_dwmac_dma_interrupt(void __iomem *ioaddr,
        return ret;
 }
 
-static void sun8i_dwmac_dma_operation_mode(void __iomem *ioaddr, int txmode,
-                                          int rxmode, int rxfifosz)
+static void sun8i_dwmac_dma_operation_mode_rx(void __iomem *ioaddr, int mode,
+                                             u32 channel, int fifosz, u8 qmode)
+{
+       u32 v;
+
+       v = readl(ioaddr + EMAC_RX_CTL1);
+       if (mode == SF_DMA_MODE) {
+               v |= EMAC_RX_MD;
+       } else {
+               v &= ~EMAC_RX_MD;
+               v &= ~EMAC_RX_TH_MASK;
+               if (mode < 32)
+                       v |= EMAC_RX_TH_32;
+               else if (mode < 64)
+                       v |= EMAC_RX_TH_64;
+               else if (mode < 96)
+                       v |= EMAC_RX_TH_96;
+               else if (mode < 128)
+                       v |= EMAC_RX_TH_128;
+       }
+       writel(v, ioaddr + EMAC_RX_CTL1);
+}
+
+static void sun8i_dwmac_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
+                                             u32 channel, int fifosz, u8 qmode)
 {
        u32 v;
 
        v = readl(ioaddr + EMAC_TX_CTL1);
-       if (txmode == SF_DMA_MODE) {
+       if (mode == SF_DMA_MODE) {
                v |= EMAC_TX_MD;
                /* Undocumented bit (called TX_NEXT_FRM in BSP), the original
                 * comment is
@@ -415,40 +488,26 @@ static void sun8i_dwmac_dma_operation_mode(void __iomem *ioaddr, int txmode,
        } else {
                v &= ~EMAC_TX_MD;
                v &= ~EMAC_TX_TH_MASK;
-               if (txmode < 64)
+               if (mode < 64)
                        v |= EMAC_TX_TH_64;
-               else if (txmode < 128)
+               else if (mode < 128)
                        v |= EMAC_TX_TH_128;
-               else if (txmode < 192)
+               else if (mode < 192)
                        v |= EMAC_TX_TH_192;
-               else if (txmode < 256)
+               else if (mode < 256)
                        v |= EMAC_TX_TH_256;
        }
        writel(v, ioaddr + EMAC_TX_CTL1);
-
-       v = readl(ioaddr + EMAC_RX_CTL1);
-       if (rxmode == SF_DMA_MODE) {
-               v |= EMAC_RX_MD;
-       } else {
-               v &= ~EMAC_RX_MD;
-               v &= ~EMAC_RX_TH_MASK;
-               if (rxmode < 32)
-                       v |= EMAC_RX_TH_32;
-               else if (rxmode < 64)
-                       v |= EMAC_RX_TH_64;
-               else if (rxmode < 96)
-                       v |= EMAC_RX_TH_96;
-               else if (rxmode < 128)
-                       v |= EMAC_RX_TH_128;
-       }
-       writel(v, ioaddr + EMAC_RX_CTL1);
 }
 
 static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = {
        .reset = sun8i_dwmac_dma_reset,
        .init = sun8i_dwmac_dma_init,
+       .init_rx_chan = sun8i_dwmac_dma_init_rx,
+       .init_tx_chan = sun8i_dwmac_dma_init_tx,
        .dump_regs = sun8i_dwmac_dump_regs,
-       .dma_mode = sun8i_dwmac_dma_operation_mode,
+       .dma_rx_mode = sun8i_dwmac_dma_operation_mode_rx,
+       .dma_tx_mode = sun8i_dwmac_dma_operation_mode_tx,
        .enable_dma_transmission = sun8i_dwmac_enable_dma_transmission,
        .enable_dma_irq = sun8i_dwmac_enable_dma_irq,
        .disable_dma_irq = sun8i_dwmac_disable_dma_irq,
@@ -745,7 +804,7 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child,
        bool need_power_ephy = false;
 
        if (current_child ^ desired_child) {
-               regmap_read(gmac->regmap, SYSCON_EMAC_REG, &reg);
+               regmap_field_read(gmac->regmap_field, &reg);
                switch (desired_child) {
                case DWMAC_SUN8I_MDIO_MUX_INTERNAL_ID:
                        dev_info(priv->device, "Switch mux to internal PHY");
@@ -763,7 +822,7 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child,
                                desired_child);
                        return -EINVAL;
                }
-               regmap_write(gmac->regmap, SYSCON_EMAC_REG, val);
+               regmap_field_write(gmac->regmap_field, val);
                if (need_power_ephy) {
                        ret = sun8i_dwmac_power_internal_phy(priv);
                        if (ret)
@@ -801,7 +860,7 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
        int ret;
        u32 reg, val;
 
-       regmap_read(gmac->regmap, SYSCON_EMAC_REG, &val);
+       regmap_field_read(gmac->regmap_field, &val);
        reg = gmac->variant->default_syscon_value;
        if (reg != val)
                dev_warn(priv->device,
@@ -835,8 +894,9 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
                }
                val /= 100;
                dev_dbg(priv->device, "set tx-delay to %x\n", val);
-               if (val <= SYSCON_ETXDC_MASK) {
-                       reg &= ~(SYSCON_ETXDC_MASK << SYSCON_ETXDC_SHIFT);
+               if (val <= gmac->variant->tx_delay_max) {
+                       reg &= ~(gmac->variant->tx_delay_max <<
+                                SYSCON_ETXDC_SHIFT);
                        reg |= (val << SYSCON_ETXDC_SHIFT);
                } else {
                        dev_err(priv->device, "Invalid TX clock delay: %d\n",
@@ -852,8 +912,9 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
                }
                val /= 100;
                dev_dbg(priv->device, "set rx-delay to %x\n", val);
-               if (val <= SYSCON_ERXDC_MASK) {
-                       reg &= ~(SYSCON_ERXDC_MASK << SYSCON_ERXDC_SHIFT);
+               if (val <= gmac->variant->rx_delay_max) {
+                       reg &= ~(gmac->variant->rx_delay_max <<
+                                SYSCON_ERXDC_SHIFT);
                        reg |= (val << SYSCON_ERXDC_SHIFT);
                } else {
                        dev_err(priv->device, "Invalid RX clock delay: %d\n",
@@ -883,7 +944,7 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
                return -EINVAL;
        }
 
-       regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg);
+       regmap_field_write(gmac->regmap_field, reg);
 
        return 0;
 }
@@ -892,7 +953,7 @@ static void sun8i_dwmac_unset_syscon(struct sunxi_priv_data *gmac)
 {
        u32 reg = gmac->variant->default_syscon_value;
 
-       regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg);
+       regmap_field_write(gmac->regmap_field, reg);
 }
 
 static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv)
@@ -971,6 +1032,34 @@ static struct mac_device_info *sun8i_dwmac_setup(void *ppriv)
        return mac;
 }
 
+static struct regmap *sun8i_dwmac_get_syscon_from_dev(struct device_node *node)
+{
+       struct device_node *syscon_node;
+       struct platform_device *syscon_pdev;
+       struct regmap *regmap = NULL;
+
+       syscon_node = of_parse_phandle(node, "syscon", 0);
+       if (!syscon_node)
+               return ERR_PTR(-ENODEV);
+
+       syscon_pdev = of_find_device_by_node(syscon_node);
+       if (!syscon_pdev) {
+               /* platform device might not be probed yet */
+               regmap = ERR_PTR(-EPROBE_DEFER);
+               goto out_put_node;
+       }
+
+       /* If no regmap is found then the other device driver is at fault */
+       regmap = dev_get_regmap(&syscon_pdev->dev, NULL);
+       if (!regmap)
+               regmap = ERR_PTR(-EINVAL);
+
+       platform_device_put(syscon_pdev);
+out_put_node:
+       of_node_put(syscon_node);
+       return regmap;
+}
+
 static int sun8i_dwmac_probe(struct platform_device *pdev)
 {
        struct plat_stmmacenet_data *plat_dat;
@@ -980,6 +1069,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
        int ret;
        struct stmmac_priv *priv;
        struct net_device *ndev;
+       struct regmap *regmap;
 
        ret = stmmac_get_platform_resources(pdev, &stmmac_res);
        if (ret)
@@ -1014,14 +1104,41 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
                gmac->regulator = NULL;
        }
 
-       gmac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
-                                                      "syscon");
-       if (IS_ERR(gmac->regmap)) {
-               ret = PTR_ERR(gmac->regmap);
+       /* The "GMAC clock control" register might be located in the
+        * CCU address range (on the R40), or the system control address
+        * range (on most other sun8i and later SoCs).
+        *
+        * The former controls most if not all clocks in the SoC. The
+        * latter has an SoC identification register, and on some SoCs,
+        * controls to map device specific SRAM to either the intended
+        * peripheral, or the CPU address space.
+        *
+        * In either case, there should be a coordinated and restricted
+        * method of accessing the register needed here. This is done by
+        * having the device export a custom regmap, instead of a generic
+        * syscon, which grants all access to all registers.
+        *
+        * To support old device trees, we fall back to using the syscon
+        * interface if possible.
+        */
+       regmap = sun8i_dwmac_get_syscon_from_dev(pdev->dev.of_node);
+       if (IS_ERR(regmap))
+               regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+                                                        "syscon");
+       if (IS_ERR(regmap)) {
+               ret = PTR_ERR(regmap);
                dev_err(&pdev->dev, "Unable to map syscon: %d\n", ret);
                return ret;
        }
 
+       gmac->regmap_field = devm_regmap_field_alloc(dev, regmap,
+                                                    *gmac->variant->syscon_field);
+       if (IS_ERR(gmac->regmap_field)) {
+               ret = PTR_ERR(gmac->regmap_field);
+               dev_err(dev, "Unable to map syscon register: %d\n", ret);
+               return ret;
+       }
+
        plat_dat->interface = of_get_phy_mode(dev->of_node);
 
        /* platform data specifying hardware features and callbacks.
@@ -1078,6 +1195,8 @@ static const struct of_device_id sun8i_dwmac_match[] = {
                .data = &emac_variant_v3s },
        { .compatible = "allwinner,sun8i-a83t-emac",
                .data = &emac_variant_a83t },
+       { .compatible = "allwinner,sun8i-r40-gmac",
+               .data = &emac_variant_r40 },
        { .compatible = "allwinner,sun50i-a64-emac",
                .data = &emac_variant_a64 },
        { }
index 7ecf549..aacc4aa 100644 (file)
@@ -81,8 +81,7 @@ static void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
 }
 
 static void dwmac1000_dma_init(void __iomem *ioaddr,
-                              struct stmmac_dma_cfg *dma_cfg,
-                              u32 dma_tx, u32 dma_rx, int atds)
+                              struct stmmac_dma_cfg *dma_cfg, int atds)
 {
        u32 value = readl(ioaddr + DMA_BUS_MODE);
        int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl;
@@ -119,12 +118,22 @@ static void dwmac1000_dma_init(void __iomem *ioaddr,
 
        /* Mask interrupts by writing to CSR7 */
        writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
+}
 
-       /* RX/TX descriptor base address lists must be written into
-        * DMA CSR3 and CSR4, respectively
-        */
-       writel(dma_tx, ioaddr + DMA_TX_BASE_ADDR);
-       writel(dma_rx, ioaddr + DMA_RCV_BASE_ADDR);
+static void dwmac1000_dma_init_rx(void __iomem *ioaddr,
+                                 struct stmmac_dma_cfg *dma_cfg,
+                                 u32 dma_rx_phy, u32 chan)
+{
+       /* RX descriptor base address list must be written into DMA CSR3 */
+       writel(dma_rx_phy, ioaddr + DMA_RCV_BASE_ADDR);
+}
+
+static void dwmac1000_dma_init_tx(void __iomem *ioaddr,
+                                 struct stmmac_dma_cfg *dma_cfg,
+                                 u32 dma_tx_phy, u32 chan)
+{
+       /* TX descriptor base address list must be written into DMA CSR4 */
+       writel(dma_tx_phy, ioaddr + DMA_TX_BASE_ADDR);
 }
 
 static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
@@ -148,12 +157,40 @@ static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
        return csr6;
 }
 
-static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
-                                        int rxmode, int rxfifosz)
+static void dwmac1000_dma_operation_mode_rx(void __iomem *ioaddr, int mode,
+                                           u32 channel, int fifosz, u8 qmode)
+{
+       u32 csr6 = readl(ioaddr + DMA_CONTROL);
+
+       if (mode == SF_DMA_MODE) {
+               pr_debug("GMAC: enable RX store and forward mode\n");
+               csr6 |= DMA_CONTROL_RSF;
+       } else {
+               pr_debug("GMAC: disable RX SF mode (threshold %d)\n", mode);
+               csr6 &= ~DMA_CONTROL_RSF;
+               csr6 &= DMA_CONTROL_TC_RX_MASK;
+               if (mode <= 32)
+                       csr6 |= DMA_CONTROL_RTC_32;
+               else if (mode <= 64)
+                       csr6 |= DMA_CONTROL_RTC_64;
+               else if (mode <= 96)
+                       csr6 |= DMA_CONTROL_RTC_96;
+               else
+                       csr6 |= DMA_CONTROL_RTC_128;
+       }
+
+       /* Configure flow control based on rx fifo size */
+       csr6 = dwmac1000_configure_fc(csr6, fifosz);
+
+       writel(csr6, ioaddr + DMA_CONTROL);
+}
+
+static void dwmac1000_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
+                                           u32 channel, int fifosz, u8 qmode)
 {
        u32 csr6 = readl(ioaddr + DMA_CONTROL);
 
-       if (txmode == SF_DMA_MODE) {
+       if (mode == SF_DMA_MODE) {
                pr_debug("GMAC: enable TX store and forward mode\n");
                /* Transmit COE type 2 cannot be done in cut-through mode. */
                csr6 |= DMA_CONTROL_TSF;
@@ -162,42 +199,22 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
                 */
                csr6 |= DMA_CONTROL_OSF;
        } else {
-               pr_debug("GMAC: disabling TX SF (threshold %d)\n", txmode);
+               pr_debug("GMAC: disabling TX SF (threshold %d)\n", mode);
                csr6 &= ~DMA_CONTROL_TSF;
                csr6 &= DMA_CONTROL_TC_TX_MASK;
                /* Set the transmit threshold */
-               if (txmode <= 32)
+               if (mode <= 32)
                        csr6 |= DMA_CONTROL_TTC_32;
-               else if (txmode <= 64)
+               else if (mode <= 64)
                        csr6 |= DMA_CONTROL_TTC_64;
-               else if (txmode <= 128)
+               else if (mode <= 128)
                        csr6 |= DMA_CONTROL_TTC_128;
-               else if (txmode <= 192)
+               else if (mode <= 192)
                        csr6 |= DMA_CONTROL_TTC_192;
                else
                        csr6 |= DMA_CONTROL_TTC_256;
        }
 
-       if (rxmode == SF_DMA_MODE) {
-               pr_debug("GMAC: enable RX store and forward mode\n");
-               csr6 |= DMA_CONTROL_RSF;
-       } else {
-               pr_debug("GMAC: disable RX SF mode (threshold %d)\n", rxmode);
-               csr6 &= ~DMA_CONTROL_RSF;
-               csr6 &= DMA_CONTROL_TC_RX_MASK;
-               if (rxmode <= 32)
-                       csr6 |= DMA_CONTROL_RTC_32;
-               else if (rxmode <= 64)
-                       csr6 |= DMA_CONTROL_RTC_64;
-               else if (rxmode <= 96)
-                       csr6 |= DMA_CONTROL_RTC_96;
-               else
-                       csr6 |= DMA_CONTROL_RTC_128;
-       }
-
-       /* Configure flow control based on rx fifo size */
-       csr6 = dwmac1000_configure_fc(csr6, rxfifosz);
-
        writel(csr6, ioaddr + DMA_CONTROL);
 }
 
@@ -256,9 +273,12 @@ static void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt,
 const struct stmmac_dma_ops dwmac1000_dma_ops = {
        .reset = dwmac_dma_reset,
        .init = dwmac1000_dma_init,
+       .init_rx_chan = dwmac1000_dma_init_rx,
+       .init_tx_chan = dwmac1000_dma_init_tx,
        .axi = dwmac1000_dma_axi,
        .dump_regs = dwmac1000_dump_dma_regs,
-       .dma_mode = dwmac1000_dma_operation_mode,
+       .dma_rx_mode = dwmac1000_dma_operation_mode_rx,
+       .dma_tx_mode = dwmac1000_dma_operation_mode_tx,
        .enable_dma_transmission = dwmac_enable_dma_transmission,
        .enable_dma_irq = dwmac_enable_dma_irq,
        .disable_dma_irq = dwmac_disable_dma_irq,
index 6502b9a..21dee25 100644 (file)
@@ -29,8 +29,7 @@
 #include "dwmac_dma.h"
 
 static void dwmac100_dma_init(void __iomem *ioaddr,
-                             struct stmmac_dma_cfg *dma_cfg,
-                             u32 dma_tx, u32 dma_rx, int atds)
+                             struct stmmac_dma_cfg *dma_cfg, int atds)
 {
        /* Enable Application Access by writing to DMA CSR0 */
        writel(DMA_BUS_MODE_DEFAULT | (dma_cfg->pbl << DMA_BUS_MODE_PBL_SHIFT),
@@ -38,12 +37,22 @@ static void dwmac100_dma_init(void __iomem *ioaddr,
 
        /* Mask interrupts by writing to CSR7 */
        writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
+}
 
-       /* RX/TX descriptor base addr lists must be written into
-        * DMA CSR3 and CSR4, respectively
-        */
-       writel(dma_tx, ioaddr + DMA_TX_BASE_ADDR);
-       writel(dma_rx, ioaddr + DMA_RCV_BASE_ADDR);
+static void dwmac100_dma_init_rx(void __iomem *ioaddr,
+                                struct stmmac_dma_cfg *dma_cfg,
+                                u32 dma_rx_phy, u32 chan)
+{
+       /* RX descriptor base addr lists must be written into DMA CSR3 */
+       writel(dma_rx_phy, ioaddr + DMA_RCV_BASE_ADDR);
+}
+
+static void dwmac100_dma_init_tx(void __iomem *ioaddr,
+                                struct stmmac_dma_cfg *dma_cfg,
+                                u32 dma_tx_phy, u32 chan)
+{
+       /* TX descriptor base addr lists must be written into DMA CSR4 */
+       writel(dma_tx_phy, ioaddr + DMA_TX_BASE_ADDR);
 }
 
 /* Store and Forward capability is not used at all.
@@ -51,14 +60,14 @@ static void dwmac100_dma_init(void __iomem *ioaddr,
  * The transmit threshold can be programmed by setting the TTC bits in the DMA
  * control register.
  */
-static void dwmac100_dma_operation_mode(void __iomem *ioaddr, int txmode,
-                                       int rxmode, int rxfifosz)
+static void dwmac100_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
+                                          u32 channel, int fifosz, u8 qmode)
 {
        u32 csr6 = readl(ioaddr + DMA_CONTROL);
 
-       if (txmode <= 32)
+       if (mode <= 32)
                csr6 |= DMA_CONTROL_TTC_32;
-       else if (txmode <= 64)
+       else if (mode <= 64)
                csr6 |= DMA_CONTROL_TTC_64;
        else
                csr6 |= DMA_CONTROL_TTC_128;
@@ -112,8 +121,10 @@ static void dwmac100_dma_diagnostic_fr(void *data, struct stmmac_extra_stats *x,
 const struct stmmac_dma_ops dwmac100_dma_ops = {
        .reset = dwmac_dma_reset,
        .init = dwmac100_dma_init,
+       .init_rx_chan = dwmac100_dma_init_rx,
+       .init_tx_chan = dwmac100_dma_init_tx,
        .dump_regs = dwmac100_dump_dma_regs,
-       .dma_mode = dwmac100_dma_operation_mode,
+       .dma_tx_mode = dwmac100_dma_operation_mode_tx,
        .dma_diagnostic_fr = dwmac100_dma_diagnostic_fr,
        .enable_dma_transmission = dwmac_enable_dma_transmission,
        .enable_dma_irq = dwmac_enable_dma_irq,
index 65ed896..20299f6 100644 (file)
@@ -189,9 +189,12 @@ static void dwmac4_set_tx_owner(struct dma_desc *p)
        p->des3 |= cpu_to_le32(TDES3_OWN);
 }
 
-static void dwmac4_set_rx_owner(struct dma_desc *p)
+static void dwmac4_set_rx_owner(struct dma_desc *p, int disable_rx_ic)
 {
-       p->des3 |= cpu_to_le32(RDES3_OWN);
+       p->des3 = cpu_to_le32(RDES3_OWN | RDES3_BUFFER1_VALID_ADDR);
+
+       if (!disable_rx_ic)
+               p->des3 |= cpu_to_le32(RDES3_INT_ON_COMPLETION_EN);
 }
 
 static int dwmac4_get_tx_ls(struct dma_desc *p)
@@ -292,10 +295,7 @@ exit:
 static void dwmac4_rd_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
                                   int mode, int end)
 {
-       p->des3 = cpu_to_le32(RDES3_OWN | RDES3_BUFFER1_VALID_ADDR);
-
-       if (!disable_rx_ic)
-               p->des3 |= cpu_to_le32(RDES3_INT_ON_COMPLETION_EN);
+       dwmac4_set_rx_owner(p, disable_rx_ic);
 }
 
 static void dwmac4_rd_init_tx_desc(struct dma_desc *p, int mode, int end)
@@ -424,6 +424,25 @@ static void dwmac4_set_mss_ctxt(struct dma_desc *p, unsigned int mss)
        p->des3 = cpu_to_le32(TDES3_CONTEXT_TYPE | TDES3_CTXT_TCMSSV);
 }
 
+static void dwmac4_get_addr(struct dma_desc *p, unsigned int *addr)
+{
+       *addr = le32_to_cpu(p->des0);
+}
+
+static void dwmac4_set_addr(struct dma_desc *p, dma_addr_t addr)
+{
+       p->des0 = cpu_to_le32(addr);
+       p->des1 = 0;
+}
+
+static void dwmac4_clear(struct dma_desc *p)
+{
+       p->des0 = 0;
+       p->des1 = 0;
+       p->des2 = 0;
+       p->des3 = 0;
+}
+
 const struct stmmac_desc_ops dwmac4_desc_ops = {
        .tx_status = dwmac4_wrback_get_tx_status,
        .rx_status = dwmac4_wrback_get_rx_status,
@@ -445,6 +464,9 @@ const struct stmmac_desc_ops dwmac4_desc_ops = {
        .init_tx_desc = dwmac4_rd_init_tx_desc,
        .display_ring = dwmac4_display_ring,
        .set_mss = dwmac4_set_mss_ctxt,
+       .get_addr = dwmac4_get_addr,
+       .set_addr = dwmac4_set_addr,
+       .clear = dwmac4_clear,
 };
 
 const struct stmmac_mode_ops dwmac4_ring_mode_ops = { };
index 117c3a5..bf8e5a1 100644 (file)
@@ -94,6 +94,10 @@ static void dwmac4_dma_init_tx_chan(void __iomem *ioaddr,
 
        value = readl(ioaddr + DMA_CHAN_TX_CONTROL(chan));
        value = value | (txpbl << DMA_BUS_MODE_PBL_SHIFT);
+
+       /* Enable OSP to get best performance */
+       value |= DMA_CONTROL_OSP;
+
        writel(value, ioaddr + DMA_CHAN_TX_CONTROL(chan));
 
        writel(dma_tx_phy, ioaddr + DMA_CHAN_TX_BASE_ADDR(chan));
@@ -116,8 +120,7 @@ static void dwmac4_dma_init_channel(void __iomem *ioaddr,
 }
 
 static void dwmac4_dma_init(void __iomem *ioaddr,
-                           struct stmmac_dma_cfg *dma_cfg,
-                           u32 dma_tx, u32 dma_rx, int atds)
+                           struct stmmac_dma_cfg *dma_cfg, int atds)
 {
        u32 value = readl(ioaddr + DMA_SYS_BUS_MODE);
 
index 8474bf9..c63c1fe 100644 (file)
 #define DMA_CHAN0_DBG_STAT_RPS_SHIFT   8
 
 int dwmac4_dma_reset(void __iomem *ioaddr);
-void dwmac4_enable_dma_transmission(void __iomem *ioaddr, u32 tail_ptr);
 void dwmac4_enable_dma_irq(void __iomem *ioaddr, u32 chan);
 void dwmac410_enable_dma_irq(void __iomem *ioaddr, u32 chan);
 void dwmac4_disable_dma_irq(void __iomem *ioaddr, u32 chan);
index 3bfb3f5..77914c8 100644 (file)
@@ -292,7 +292,7 @@ static void enh_desc_set_tx_owner(struct dma_desc *p)
        p->des0 |= cpu_to_le32(ETDES0_OWN);
 }
 
-static void enh_desc_set_rx_owner(struct dma_desc *p)
+static void enh_desc_set_rx_owner(struct dma_desc *p, int disable_rx_ic)
 {
        p->des0 |= cpu_to_le32(RDES0_OWN);
 }
@@ -437,6 +437,21 @@ static void enh_desc_display_ring(void *head, unsigned int size, bool rx)
        pr_info("\n");
 }
 
+static void enh_desc_get_addr(struct dma_desc *p, unsigned int *addr)
+{
+       *addr = le32_to_cpu(p->des2);
+}
+
+static void enh_desc_set_addr(struct dma_desc *p, dma_addr_t addr)
+{
+       p->des2 = cpu_to_le32(addr);
+}
+
+static void enh_desc_clear(struct dma_desc *p)
+{
+       p->des2 = 0;
+}
+
 const struct stmmac_desc_ops enh_desc_ops = {
        .tx_status = enh_desc_get_tx_status,
        .rx_status = enh_desc_get_rx_status,
@@ -457,4 +472,7 @@ const struct stmmac_desc_ops enh_desc_ops = {
        .get_timestamp = enh_desc_get_timestamp,
        .get_rx_timestamp_status = enh_desc_get_rx_timestamp_status,
        .display_ring = enh_desc_display_ring,
+       .get_addr = enh_desc_get_addr,
+       .set_addr = enh_desc_set_addr,
+       .clear = enh_desc_clear,
 };
index 9acc8d2..14770fc 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "common.h"
 #include "stmmac.h"
+#include "stmmac_ptp.h"
 
 static u32 stmmac_get_id(struct stmmac_priv *priv, u32 id_reg)
 {
@@ -72,6 +73,7 @@ static const struct stmmac_hwif_entry {
        bool gmac;
        bool gmac4;
        u32 min_id;
+       const struct stmmac_regs_off regs;
        const void *desc;
        const void *dma;
        const void *mac;
@@ -86,6 +88,10 @@ static const struct stmmac_hwif_entry {
                .gmac = false,
                .gmac4 = false,
                .min_id = 0,
+               .regs = {
+                       .ptp_off = PTP_GMAC3_X_OFFSET,
+                       .mmc_off = MMC_GMAC3_X_OFFSET,
+               },
                .desc = NULL,
                .dma = &dwmac100_dma_ops,
                .mac = &dwmac100_ops,
@@ -98,6 +104,10 @@ static const struct stmmac_hwif_entry {
                .gmac = true,
                .gmac4 = false,
                .min_id = 0,
+               .regs = {
+                       .ptp_off = PTP_GMAC3_X_OFFSET,
+                       .mmc_off = MMC_GMAC3_X_OFFSET,
+               },
                .desc = NULL,
                .dma = &dwmac1000_dma_ops,
                .mac = &dwmac1000_ops,
@@ -110,6 +120,10 @@ static const struct stmmac_hwif_entry {
                .gmac = false,
                .gmac4 = true,
                .min_id = 0,
+               .regs = {
+                       .ptp_off = PTP_GMAC4_OFFSET,
+                       .mmc_off = MMC_GMAC4_OFFSET,
+               },
                .desc = &dwmac4_desc_ops,
                .dma = &dwmac4_dma_ops,
                .mac = &dwmac4_ops,
@@ -122,6 +136,10 @@ static const struct stmmac_hwif_entry {
                .gmac = false,
                .gmac4 = true,
                .min_id = DWMAC_CORE_4_00,
+               .regs = {
+                       .ptp_off = PTP_GMAC4_OFFSET,
+                       .mmc_off = MMC_GMAC4_OFFSET,
+               },
                .desc = &dwmac4_desc_ops,
                .dma = &dwmac4_dma_ops,
                .mac = &dwmac410_ops,
@@ -134,6 +152,10 @@ static const struct stmmac_hwif_entry {
                .gmac = false,
                .gmac4 = true,
                .min_id = DWMAC_CORE_4_10,
+               .regs = {
+                       .ptp_off = PTP_GMAC4_OFFSET,
+                       .mmc_off = MMC_GMAC4_OFFSET,
+               },
                .desc = &dwmac4_desc_ops,
                .dma = &dwmac410_dma_ops,
                .mac = &dwmac410_ops,
@@ -146,6 +168,10 @@ static const struct stmmac_hwif_entry {
                .gmac = false,
                .gmac4 = true,
                .min_id = DWMAC_CORE_5_10,
+               .regs = {
+                       .ptp_off = PTP_GMAC4_OFFSET,
+                       .mmc_off = MMC_GMAC4_OFFSET,
+               },
                .desc = &dwmac4_desc_ops,
                .dma = &dwmac410_dma_ops,
                .mac = &dwmac510_ops,
@@ -163,27 +189,35 @@ int stmmac_hwif_init(struct stmmac_priv *priv)
        bool needs_gmac = priv->plat->has_gmac;
        const struct stmmac_hwif_entry *entry;
        struct mac_device_info *mac;
+       bool needs_setup = true;
        int i, ret;
        u32 id;
 
        if (needs_gmac) {
                id = stmmac_get_id(priv, GMAC_VERSION);
-       } else {
+       } else if (needs_gmac4) {
                id = stmmac_get_id(priv, GMAC4_VERSION);
+       } else {
+               id = 0;
        }
 
        /* Save ID for later use */
        priv->synopsys_id = id;
 
+       /* Lets assume some safe values first */
+       priv->ptpaddr = priv->ioaddr +
+               (needs_gmac4 ? PTP_GMAC4_OFFSET : PTP_GMAC3_X_OFFSET);
+       priv->mmcaddr = priv->ioaddr +
+               (needs_gmac4 ? MMC_GMAC4_OFFSET : MMC_GMAC3_X_OFFSET);
+
        /* Check for HW specific setup first */
        if (priv->plat->setup) {
-               priv->hw = priv->plat->setup(priv);
-               if (!priv->hw)
-                       return -ENOMEM;
-               return 0;
+               mac = priv->plat->setup(priv);
+               needs_setup = false;
+       } else {
+               mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
        }
 
-       mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
        if (!mac)
                return -ENOMEM;
 
@@ -195,22 +229,28 @@ int stmmac_hwif_init(struct stmmac_priv *priv)
                        continue;
                if (needs_gmac4 ^ entry->gmac4)
                        continue;
-               if (id < entry->min_id)
+               /* Use synopsys_id var because some setups can override this */
+               if (priv->synopsys_id < entry->min_id)
                        continue;
 
-               mac->desc = entry->desc;
-               mac->dma = entry->dma;
-               mac->mac = entry->mac;
-               mac->ptp = entry->hwtimestamp;
-               mac->mode = entry->mode;
-               mac->tc = entry->tc;
+               /* Only use generic HW helpers if needed */
+               mac->desc = mac->desc ? : entry->desc;
+               mac->dma = mac->dma ? : entry->dma;
+               mac->mac = mac->mac ? : entry->mac;
+               mac->ptp = mac->ptp ? : entry->hwtimestamp;
+               mac->mode = mac->mode ? : entry->mode;
+               mac->tc = mac->tc ? : entry->tc;
 
                priv->hw = mac;
+               priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off;
+               priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off;
 
                /* Entry found */
-               ret = entry->setup(priv);
-               if (ret)
-                       return ret;
+               if (needs_setup) {
+                       ret = entry->setup(priv);
+                       if (ret)
+                               return ret;
+               }
 
                /* Run quirks, if needed */
                if (entry->quirks) {
index b7539a1..f499a7f 100644 (file)
@@ -59,7 +59,7 @@ struct stmmac_desc_ops {
        /* Get the buffer size from the descriptor */
        int (*get_tx_len)(struct dma_desc *p);
        /* Handle extra events on specific interrupts hw dependent */
-       void (*set_rx_owner)(struct dma_desc *p);
+       void (*set_rx_owner)(struct dma_desc *p, int disable_rx_ic);
        /* Get the receive frame size */
        int (*get_rx_frame_len)(struct dma_desc *p, int rx_coe_type);
        /* Return the reception status looking at the RDES1 */
@@ -79,6 +79,12 @@ struct stmmac_desc_ops {
        void (*display_ring)(void *head, unsigned int size, bool rx);
        /* set MSS via context descriptor */
        void (*set_mss)(struct dma_desc *p, unsigned int mss);
+       /* get descriptor skbuff address */
+       void (*get_addr)(struct dma_desc *p, unsigned int *addr);
+       /* set descriptor skbuff address */
+       void (*set_addr)(struct dma_desc *p, dma_addr_t addr);
+       /* clear descriptor */
+       void (*clear)(struct dma_desc *p);
 };
 
 #define stmmac_init_rx_desc(__priv, __args...) \
@@ -123,6 +129,12 @@ struct stmmac_desc_ops {
        stmmac_do_void_callback(__priv, desc, display_ring, __args)
 #define stmmac_set_mss(__priv, __args...) \
        stmmac_do_void_callback(__priv, desc, set_mss, __args)
+#define stmmac_get_desc_addr(__priv, __args...) \
+       stmmac_do_void_callback(__priv, desc, get_addr, __args)
+#define stmmac_set_desc_addr(__priv, __args...) \
+       stmmac_do_void_callback(__priv, desc, set_addr, __args)
+#define stmmac_clear_desc(__priv, __args...) \
+       stmmac_do_void_callback(__priv, desc, clear, __args)
 
 struct stmmac_dma_cfg;
 struct dma_features;
@@ -132,7 +144,7 @@ struct stmmac_dma_ops {
        /* DMA core initialization */
        int (*reset)(void __iomem *ioaddr);
        void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg,
-                    u32 dma_tx, u32 dma_rx, int atds);
+                    int atds);
        void (*init_chan)(void __iomem *ioaddr,
                          struct stmmac_dma_cfg *dma_cfg, u32 chan);
        void (*init_rx_chan)(void __iomem *ioaddr,
@@ -145,10 +157,6 @@ struct stmmac_dma_ops {
        void (*axi)(void __iomem *ioaddr, struct stmmac_axi *axi);
        /* Dump DMA registers */
        void (*dump_regs)(void __iomem *ioaddr, u32 *reg_space);
-       /* Set tx/rx threshold in the csr6 register
-        * An invalid value enables the store-and-forward mode */
-       void (*dma_mode)(void __iomem *ioaddr, int txmode, int rxmode,
-                        int rxfifosz);
        void (*dma_rx_mode)(void __iomem *ioaddr, int mode, u32 channel,
                            int fifosz, u8 qmode);
        void (*dma_tx_mode)(void __iomem *ioaddr, int mode, u32 channel,
@@ -191,8 +199,6 @@ struct stmmac_dma_ops {
        stmmac_do_void_callback(__priv, dma, axi, __args)
 #define stmmac_dump_dma_regs(__priv, __args...) \
        stmmac_do_void_callback(__priv, dma, dump_regs, __args)
-#define stmmac_dma_mode(__priv, __args...) \
-       stmmac_do_void_callback(__priv, dma, dma_mode, __args)
 #define stmmac_dma_rx_mode(__priv, __args...) \
        stmmac_do_void_callback(__priv, dma, dma_rx_mode, __args)
 #define stmmac_dma_tx_mode(__priv, __args...) \
@@ -440,6 +446,11 @@ struct stmmac_tc_ops {
 #define stmmac_tc_setup_cls_u32(__priv, __args...) \
        stmmac_do_callback(__priv, tc, setup_cls_u32, __args)
 
+struct stmmac_regs_off {
+       u32 ptp_off;
+       u32 mmc_off;
+};
+
 extern const struct stmmac_ops dwmac100_ops;
 extern const struct stmmac_dma_ops dwmac100_dma_ops;
 extern const struct stmmac_ops dwmac1000_ops;
index 7b1d901..de65bb2 100644 (file)
@@ -168,7 +168,7 @@ static void ndesc_set_tx_owner(struct dma_desc *p)
        p->des0 |= cpu_to_le32(TDES0_OWN);
 }
 
-static void ndesc_set_rx_owner(struct dma_desc *p)
+static void ndesc_set_rx_owner(struct dma_desc *p, int disable_rx_ic)
 {
        p->des0 |= cpu_to_le32(RDES0_OWN);
 }
@@ -297,6 +297,21 @@ static void ndesc_display_ring(void *head, unsigned int size, bool rx)
        pr_info("\n");
 }
 
+static void ndesc_get_addr(struct dma_desc *p, unsigned int *addr)
+{
+       *addr = le32_to_cpu(p->des2);
+}
+
+static void ndesc_set_addr(struct dma_desc *p, dma_addr_t addr)
+{
+       p->des2 = cpu_to_le32(addr);
+}
+
+static void ndesc_clear(struct dma_desc *p)
+{
+       p->des2 = 0;
+}
+
 const struct stmmac_desc_ops ndesc_ops = {
        .tx_status = ndesc_get_tx_status,
        .rx_status = ndesc_get_rx_status,
@@ -316,4 +331,7 @@ const struct stmmac_desc_ops ndesc_ops = {
        .get_timestamp = ndesc_get_timestamp,
        .get_rx_timestamp_status = ndesc_get_rx_timestamp_status,
        .display_ring = ndesc_display_ring,
+       .get_addr = ndesc_get_addr,
+       .set_addr = ndesc_set_addr,
+       .clear = ndesc_clear,
 };
index 42fc76e..4d425b1 100644 (file)
@@ -105,6 +105,7 @@ struct stmmac_priv {
        u32 tx_count_frames;
        u32 tx_coal_frames;
        u32 tx_coal_timer;
+       bool tx_timer_armed;
 
        int tx_coalesce;
        int hwts_tx_en;
index d9dbe13..c32de53 100644 (file)
@@ -1156,10 +1156,7 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
                return -EINVAL;
        }
 
-       if (priv->synopsys_id >= DWMAC_CORE_4_00)
-               p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[i]);
-       else
-               p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[i]);
+       stmmac_set_desc_addr(priv, p, rx_q->rx_skbuff_dma[i]);
 
        if (priv->dma_buf_sz == BUF_SIZE_16KiB)
                stmmac_init_desc3(priv, p);
@@ -1344,14 +1341,7 @@ static int init_dma_tx_desc_rings(struct net_device *dev)
                        else
                                p = tx_q->dma_tx + i;
 
-                       if (priv->synopsys_id >= DWMAC_CORE_4_00) {
-                               p->des0 = 0;
-                               p->des1 = 0;
-                               p->des2 = 0;
-                               p->des3 = 0;
-                       } else {
-                               p->des2 = 0;
-                       }
+                       stmmac_clear_desc(priv, p);
 
                        tx_q->tx_skbuff_dma[i].buf = 0;
                        tx_q->tx_skbuff_dma[i].map_as_page = false;
@@ -1797,22 +1787,18 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
        }
 
        /* configure all channels */
-       if (priv->synopsys_id >= DWMAC_CORE_4_00) {
-               for (chan = 0; chan < rx_channels_count; chan++) {
-                       qmode = priv->plat->rx_queues_cfg[chan].mode_to_use;
+       for (chan = 0; chan < rx_channels_count; chan++) {
+               qmode = priv->plat->rx_queues_cfg[chan].mode_to_use;
 
-                       stmmac_dma_rx_mode(priv, priv->ioaddr, rxmode, chan,
-                                       rxfifosz, qmode);
-               }
+               stmmac_dma_rx_mode(priv, priv->ioaddr, rxmode, chan,
+                               rxfifosz, qmode);
+       }
 
-               for (chan = 0; chan < tx_channels_count; chan++) {
-                       qmode = priv->plat->tx_queues_cfg[chan].mode_to_use;
+       for (chan = 0; chan < tx_channels_count; chan++) {
+               qmode = priv->plat->tx_queues_cfg[chan].mode_to_use;
 
-                       stmmac_dma_tx_mode(priv, priv->ioaddr, txmode, chan,
-                                       txfifosz, qmode);
-               }
-       } else {
-               stmmac_dma_mode(priv, priv->ioaddr, txmode, rxmode, rxfifosz);
+               stmmac_dma_tx_mode(priv, priv->ioaddr, txmode, chan,
+                               txfifosz, qmode);
        }
 }
 
@@ -1981,23 +1967,14 @@ static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
        rxfifosz /= rx_channels_count;
        txfifosz /= tx_channels_count;
 
-       if (priv->synopsys_id >= DWMAC_CORE_4_00) {
-               stmmac_dma_rx_mode(priv, priv->ioaddr, rxmode, chan, rxfifosz,
-                               rxqmode);
-               stmmac_dma_tx_mode(priv, priv->ioaddr, txmode, chan, txfifosz,
-                               txqmode);
-       } else {
-               stmmac_dma_mode(priv, priv->ioaddr, txmode, rxmode, rxfifosz);
-       }
+       stmmac_dma_rx_mode(priv, priv->ioaddr, rxmode, chan, rxfifosz, rxqmode);
+       stmmac_dma_tx_mode(priv, priv->ioaddr, txmode, chan, txfifosz, txqmode);
 }
 
 static bool stmmac_safety_feat_interrupt(struct stmmac_priv *priv)
 {
-       int ret = false;
+       int ret;
 
-       /* Safety features are only available in cores >= 5.10 */
-       if (priv->synopsys_id < DWMAC_CORE_5_10)
-               return ret;
        ret = stmmac_safety_feat_irq_status(priv, priv->dev,
                        priv->ioaddr, priv->dma_cap.asp, &priv->sstats);
        if (ret && (ret != -EINVAL)) {
@@ -2108,14 +2085,6 @@ static void stmmac_mmc_setup(struct stmmac_priv *priv)
        unsigned int mode = MMC_CNTRL_RESET_ON_READ | MMC_CNTRL_COUNTER_RESET |
                            MMC_CNTRL_PRESET | MMC_CNTRL_FULL_HALF_PRESET;
 
-       if (priv->synopsys_id >= DWMAC_CORE_4_00) {
-               priv->ptpaddr = priv->ioaddr + PTP_GMAC4_OFFSET;
-               priv->mmcaddr = priv->ioaddr + MMC_GMAC4_OFFSET;
-       } else {
-               priv->ptpaddr = priv->ioaddr + PTP_GMAC3_X_OFFSET;
-               priv->mmcaddr = priv->ioaddr + MMC_GMAC3_X_OFFSET;
-       }
-
        dwmac_mmc_intr_all_mask(priv->mmcaddr);
 
        if (priv->dma_cap.rmon) {
@@ -2169,10 +2138,9 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
 {
        u32 rx_channels_count = priv->plat->rx_queues_to_use;
        u32 tx_channels_count = priv->plat->tx_queues_to_use;
+       u32 dma_csr_ch = max(rx_channels_count, tx_channels_count);
        struct stmmac_rx_queue *rx_q;
        struct stmmac_tx_queue *tx_q;
-       u32 dummy_dma_rx_phy = 0;
-       u32 dummy_dma_tx_phy = 0;
        u32 chan = 0;
        int atds = 0;
        int ret = 0;
@@ -2191,48 +2159,39 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
                return ret;
        }
 
-       if (priv->synopsys_id >= DWMAC_CORE_4_00) {
-               /* DMA Configuration */
-               stmmac_dma_init(priv, priv->ioaddr, priv->plat->dma_cfg,
-                               dummy_dma_tx_phy, dummy_dma_rx_phy, atds);
-
-               /* DMA RX Channel Configuration */
-               for (chan = 0; chan < rx_channels_count; chan++) {
-                       rx_q = &priv->rx_queue[chan];
-
-                       stmmac_init_rx_chan(priv, priv->ioaddr,
-                                       priv->plat->dma_cfg, rx_q->dma_rx_phy,
-                                       chan);
-
-                       rx_q->rx_tail_addr = rx_q->dma_rx_phy +
-                                   (DMA_RX_SIZE * sizeof(struct dma_desc));
-                       stmmac_set_rx_tail_ptr(priv, priv->ioaddr,
-                                       rx_q->rx_tail_addr, chan);
-               }
-
-               /* DMA TX Channel Configuration */
-               for (chan = 0; chan < tx_channels_count; chan++) {
-                       tx_q = &priv->tx_queue[chan];
+       /* DMA RX Channel Configuration */
+       for (chan = 0; chan < rx_channels_count; chan++) {
+               rx_q = &priv->rx_queue[chan];
 
-                       stmmac_init_chan(priv, priv->ioaddr,
-                                       priv->plat->dma_cfg, chan);
+               stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+                                   rx_q->dma_rx_phy, chan);
 
-                       stmmac_init_tx_chan(priv, priv->ioaddr,
-                                       priv->plat->dma_cfg, tx_q->dma_tx_phy,
-                                       chan);
+               rx_q->rx_tail_addr = rx_q->dma_rx_phy +
+                           (DMA_RX_SIZE * sizeof(struct dma_desc));
+               stmmac_set_rx_tail_ptr(priv, priv->ioaddr,
+                                      rx_q->rx_tail_addr, chan);
+       }
 
-                       tx_q->tx_tail_addr = tx_q->dma_tx_phy +
-                                   (DMA_TX_SIZE * sizeof(struct dma_desc));
-                       stmmac_set_tx_tail_ptr(priv, priv->ioaddr,
-                                       tx_q->tx_tail_addr, chan);
-               }
-       } else {
-               rx_q = &priv->rx_queue[chan];
+       /* DMA TX Channel Configuration */
+       for (chan = 0; chan < tx_channels_count; chan++) {
                tx_q = &priv->tx_queue[chan];
-               stmmac_dma_init(priv, priv->ioaddr, priv->plat->dma_cfg,
-                               tx_q->dma_tx_phy, rx_q->dma_rx_phy, atds);
+
+               stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+                                   tx_q->dma_tx_phy, chan);
+
+               tx_q->tx_tail_addr = tx_q->dma_tx_phy +
+                           (DMA_TX_SIZE * sizeof(struct dma_desc));
+               stmmac_set_tx_tail_ptr(priv, priv->ioaddr,
+                                      tx_q->tx_tail_addr, chan);
        }
 
+       /* DMA CSR Channel configuration */
+       for (chan = 0; chan < dma_csr_ch; chan++)
+               stmmac_init_chan(priv, priv->ioaddr, priv->plat->dma_cfg, chan);
+
+       /* DMA Configuration */
+       stmmac_dma_init(priv, priv->ioaddr, priv->plat->dma_cfg, atds);
+
        if (priv->plat->axi)
                stmmac_axi(priv, priv->ioaddr, priv->plat->axi);
 
@@ -2515,12 +2474,10 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
        stmmac_core_init(priv, priv->hw, dev);
 
        /* Initialize MTL*/
-       if (priv->synopsys_id >= DWMAC_CORE_4_00)
-               stmmac_mtl_configuration(priv);
+       stmmac_mtl_configuration(priv);
 
        /* Initialize Safety Features */
-       if (priv->synopsys_id >= DWMAC_CORE_5_10)
-               stmmac_safety_feat_configuration(priv);
+       stmmac_safety_feat_configuration(priv);
 
        ret = stmmac_rx_ipc(priv, priv->hw);
        if (!ret) {
@@ -3074,10 +3031,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
        if (enh_desc)
                is_jumbo = stmmac_is_jumbo_frm(priv, skb->len, enh_desc);
 
-       if (unlikely(is_jumbo) && likely(priv->synopsys_id <
-                                        DWMAC_CORE_4_00)) {
+       if (unlikely(is_jumbo)) {
                entry = stmmac_jumbo_frm(priv, tx_q, skb, csum_insertion);
-               if (unlikely(entry < 0))
+               if (unlikely(entry < 0) && (entry != -EINVAL))
                        goto dma_map_err;
        }
 
@@ -3100,10 +3056,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
                        goto dma_map_err; /* should reuse desc w/o issues */
 
                tx_q->tx_skbuff_dma[entry].buf = des;
-               if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
-                       desc->des0 = cpu_to_le32(des);
-               else
-                       desc->des2 = cpu_to_le32(des);
+
+               stmmac_set_desc_addr(priv, desc, des);
 
                tx_q->tx_skbuff_dma[entry].map_as_page = true;
                tx_q->tx_skbuff_dma[entry].len = len;
@@ -3158,13 +3112,16 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
         * element in case of no SG.
         */
        priv->tx_count_frames += nfrags + 1;
-       if (likely(priv->tx_coal_frames > priv->tx_count_frames)) {
+       if (likely(priv->tx_coal_frames > priv->tx_count_frames) &&
+           !priv->tx_timer_armed) {
                mod_timer(&priv->txtimer,
                          STMMAC_COAL_TIMER(priv->tx_coal_timer));
+               priv->tx_timer_armed = true;
        } else {
                priv->tx_count_frames = 0;
                stmmac_set_tx_ic(priv, desc);
                priv->xstats.tx_set_ic_bit++;
+               priv->tx_timer_armed = false;
        }
 
        skb_tx_timestamp(skb);
@@ -3182,10 +3139,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
                        goto dma_map_err;
 
                tx_q->tx_skbuff_dma[first_entry].buf = des;
-               if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
-                       first->des0 = cpu_to_le32(des);
-               else
-                       first->des2 = cpu_to_le32(des);
+
+               stmmac_set_desc_addr(priv, first, des);
 
                tx_q->tx_skbuff_dma[first_entry].len = nopaged_len;
                tx_q->tx_skbuff_dma[first_entry].last_segment = last_segment;
@@ -3211,11 +3166,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
 
        netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len);
 
-       if (priv->synopsys_id < DWMAC_CORE_4_00)
-               stmmac_enable_dma_transmission(priv, priv->ioaddr);
-       else
-               stmmac_set_tx_tail_ptr(priv, priv->ioaddr, tx_q->tx_tail_addr,
-                               queue);
+       stmmac_enable_dma_transmission(priv, priv->ioaddr);
+       stmmac_set_tx_tail_ptr(priv, priv->ioaddr, tx_q->tx_tail_addr, queue);
 
        return NETDEV_TX_OK;
 
@@ -3299,13 +3251,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
                                break;
                        }
 
-                       if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) {
-                               p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[entry]);
-                               p->des1 = 0;
-                       } else {
-                               p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[entry]);
-                       }
-
+                       stmmac_set_desc_addr(priv, p, rx_q->rx_skbuff_dma[entry]);
                        stmmac_refill_desc3(priv, rx_q, p);
 
                        if (rx_q->rx_zeroc_thresh > 0)
@@ -3316,10 +3262,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
                }
                dma_wmb();
 
-               if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
-                       stmmac_init_rx_desc(priv, p, priv->use_riwt, 0, 0);
-               else
-                       stmmac_set_rx_owner(priv, p);
+               stmmac_set_rx_owner(priv, p, priv->use_riwt);
 
                dma_wmb();
 
@@ -3407,11 +3350,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
                        int frame_len;
                        unsigned int des;
 
-                       if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
-                               des = le32_to_cpu(p->des0);
-                       else
-                               des = le32_to_cpu(p->des2);
-
+                       stmmac_get_desc_addr(priv, p, &des);
                        frame_len = stmmac_get_rx_frame_len(priv, p, coe);
 
                        /*  If frame length is greater than skb buffer size
@@ -3705,6 +3644,7 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
        /* To handle GMAC own interrupts */
        if ((priv->plat->has_gmac) || (priv->plat->has_gmac4)) {
                int status = stmmac_host_irq_status(priv, priv->hw, &priv->xstats);
+               int mtl_status;
 
                if (unlikely(status)) {
                        /* For LPI we need to save the tx status */
@@ -3714,20 +3654,18 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
                                priv->tx_path_in_lpi_mode = false;
                }
 
-               if (priv->synopsys_id >= DWMAC_CORE_4_00) {
-                       for (queue = 0; queue < queues_count; queue++) {
-                               struct stmmac_rx_queue *rx_q =
-                               &priv->rx_queue[queue];
+               for (queue = 0; queue < queues_count; queue++) {
+                       struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
 
-                               status |= stmmac_host_mtl_irq_status(priv,
-                                               priv->hw, queue);
+                       mtl_status = stmmac_host_mtl_irq_status(priv, priv->hw,
+                                                               queue);
+                       if (mtl_status != -EINVAL)
+                               status |= mtl_status;
 
-                               if (status & CORE_IRQ_MTL_RX_OVERFLOW)
-                                       stmmac_set_rx_tail_ptr(priv,
-                                                       priv->ioaddr,
-                                                       rx_q->rx_tail_addr,
-                                                       queue);
-                       }
+                       if (status & CORE_IRQ_MTL_RX_OVERFLOW)
+                               stmmac_set_rx_tail_ptr(priv, priv->ioaddr,
+                                                      rx_q->rx_tail_addr,
+                                                      queue);
                }
 
                /* PCS link status */
index f5f37bf..5df1a60 100644 (file)
@@ -233,10 +233,7 @@ int stmmac_mdio_register(struct net_device *ndev)
        new_bus->phy_mask = mdio_bus_data->phy_mask;
        new_bus->parent = priv->device;
 
-       if (mdio_node)
-               err = of_mdiobus_register(new_bus, mdio_node);
-       else
-               err = mdiobus_register(new_bus);
+       err = of_mdiobus_register(new_bus, mdio_node);
        if (err != 0) {
                dev_err(dev, "Cannot register the MDIO bus\n");
                goto bus_register_fail;
index 48a541e..9263d63 100644 (file)
@@ -18,7 +18,7 @@ if NET_VENDOR_TI
 
 config TI_DAVINCI_EMAC
        tristate "TI DaVinci EMAC Support"
-       depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
+       depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 ) || COMPILE_TEST
        select TI_DAVINCI_MDIO
        select TI_DAVINCI_CPDMA
        select PHYLIB
@@ -30,7 +30,7 @@ config TI_DAVINCI_EMAC
 
 config TI_DAVINCI_MDIO
        tristate "TI DaVinci MDIO Support"
-       depends on ARCH_DAVINCI || ARCH_OMAP2PLUS || ARCH_KEYSTONE
+       depends on ARCH_DAVINCI || ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
        select PHYLIB
        ---help---
          This driver supports TI's DaVinci MDIO module.
@@ -40,7 +40,7 @@ config TI_DAVINCI_MDIO
 
 config TI_DAVINCI_CPDMA
        tristate "TI DaVinci CPDMA Support"
-       depends on ARCH_DAVINCI || ARCH_OMAP2PLUS
+       depends on ARCH_DAVINCI || ARCH_OMAP2PLUS || COMPILE_TEST
        ---help---
          This driver supports TI's DaVinci CPDMA dma engine.
 
@@ -60,7 +60,7 @@ config TI_CPSW_ALE
 
 config TI_CPSW
        tristate "TI CPSW Switch Support"
-       depends on ARCH_DAVINCI || ARCH_OMAP2PLUS
+       depends on ARCH_DAVINCI || ARCH_OMAP2PLUS || COMPILE_TEST
        select TI_DAVINCI_CPDMA
        select TI_DAVINCI_MDIO
        select TI_CPSW_PHY_SEL
@@ -75,7 +75,7 @@ config TI_CPSW
 
 config TI_CPTS
        bool "TI Common Platform Time Sync (CPTS) Support"
-       depends on TI_CPSW || TI_KEYSTONE_NETCP
+       depends on TI_CPSW || TI_KEYSTONE_NETCP || COMPILE_TEST
        depends on POSIX_TIMERS
        ---help---
          This driver supports the Common Platform Time Sync unit of
index 1801364..0c1adad 100644 (file)
@@ -177,12 +177,18 @@ void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave)
        }
 
        dev = bus_find_device(&platform_bus_type, NULL, node, match);
-       of_node_put(node);
+       if (!dev) {
+               dev_err(dev, "unable to find platform device for %pOF\n", node);
+               goto out;
+       }
+
        priv = dev_get_drvdata(dev);
 
        priv->cpsw_phy_sel(priv, phy_mode, slave);
 
        put_device(dev);
+out:
+       of_node_put(node);
 }
 EXPORT_SYMBOL_GPL(cpsw_phy_sel);
 
index 28d893b..643cd2d 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/of_device.h>
 #include <linux/if_vlan.h>
 #include <linux/kmemleak.h>
+#include <linux/sys_soc.h>
 
 #include <linux/pinctrl/consumer.h>
 
@@ -957,7 +958,7 @@ static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget)
+static int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget)
 {
        u32                     ch_map;
        int                     num_tx, cur_budget, ch;
@@ -984,7 +985,21 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget)
        if (num_tx < budget) {
                napi_complete(napi_tx);
                writel(0xff, &cpsw->wr_regs->tx_en);
-               if (cpsw->quirk_irq && cpsw->tx_irq_disabled) {
+       }
+
+       return num_tx;
+}
+
+static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget)
+{
+       struct cpsw_common *cpsw = napi_to_cpsw(napi_tx);
+       int num_tx;
+
+       num_tx = cpdma_chan_process(cpsw->txv[0].ch, budget);
+       if (num_tx < budget) {
+               napi_complete(napi_tx);
+               writel(0xff, &cpsw->wr_regs->tx_en);
+               if (cpsw->tx_irq_disabled) {
                        cpsw->tx_irq_disabled = false;
                        enable_irq(cpsw->irqs_table[1]);
                }
@@ -993,7 +1008,7 @@ static int cpsw_tx_poll(struct napi_struct *napi_tx, int budget)
        return num_tx;
 }
 
-static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget)
+static int cpsw_rx_mq_poll(struct napi_struct *napi_rx, int budget)
 {
        u32                     ch_map;
        int                     num_rx, cur_budget, ch;
@@ -1020,7 +1035,21 @@ static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget)
        if (num_rx < budget) {
                napi_complete_done(napi_rx, num_rx);
                writel(0xff, &cpsw->wr_regs->rx_en);
-               if (cpsw->quirk_irq && cpsw->rx_irq_disabled) {
+       }
+
+       return num_rx;
+}
+
+static int cpsw_rx_poll(struct napi_struct *napi_rx, int budget)
+{
+       struct cpsw_common *cpsw = napi_to_cpsw(napi_rx);
+       int num_rx;
+
+       num_rx = cpdma_chan_process(cpsw->rxv[0].ch, budget);
+       if (num_rx < budget) {
+               napi_complete_done(napi_rx, num_rx);
+               writel(0xff, &cpsw->wr_regs->rx_en);
+               if (cpsw->rx_irq_disabled) {
                        cpsw->rx_irq_disabled = false;
                        enable_irq(cpsw->irqs_table[0]);
                }
@@ -1252,8 +1281,8 @@ static void cpsw_add_ch_strings(u8 **p, int ch_num, int rx_dir)
        for (i = 0; i < ch_stats_len; i++) {
                line = i % CPSW_STATS_CH_LEN;
                snprintf(*p, ETH_GSTRING_LEN,
-                        "%s DMA chan %d: %s", rx_dir ? "Rx" : "Tx",
-                        i / CPSW_STATS_CH_LEN,
+                        "%s DMA chan %ld: %s", rx_dir ? "Rx" : "Tx",
+                        (long)(i / CPSW_STATS_CH_LEN),
                         cpsw_gstrings_ch_stats[line].stat_string);
                *p += ETH_GSTRING_LEN;
        }
@@ -2364,9 +2393,9 @@ static void cpsw_get_channels(struct net_device *ndev,
 {
        struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
 
+       ch->max_rx = cpsw->quirk_irq ? 1 : CPSW_MAX_QUEUES;
+       ch->max_tx = cpsw->quirk_irq ? 1 : CPSW_MAX_QUEUES;
        ch->max_combined = 0;
-       ch->max_rx = CPSW_MAX_QUEUES;
-       ch->max_tx = CPSW_MAX_QUEUES;
        ch->max_other = 0;
        ch->other_count = 0;
        ch->rx_count = cpsw->rx_ch_num;
@@ -2377,6 +2406,11 @@ static void cpsw_get_channels(struct net_device *ndev,
 static int cpsw_check_ch_settings(struct cpsw_common *cpsw,
                                  struct ethtool_channels *ch)
 {
+       if (cpsw->quirk_irq) {
+               dev_err(cpsw->dev, "Maximum one tx/rx queue is allowed");
+               return -EOPNOTSUPP;
+       }
+
        if (ch->combined_count)
                return -EINVAL;
 
@@ -2917,44 +2951,20 @@ static int cpsw_probe_dual_emac(struct cpsw_priv *priv)
        return ret;
 }
 
-#define CPSW_QUIRK_IRQ         BIT(0)
-
-static const struct platform_device_id cpsw_devtype[] = {
-       {
-               /* keep it for existing comaptibles */
-               .name = "cpsw",
-               .driver_data = CPSW_QUIRK_IRQ,
-       }, {
-               .name = "am335x-cpsw",
-               .driver_data = CPSW_QUIRK_IRQ,
-       }, {
-               .name = "am4372-cpsw",
-               .driver_data = 0,
-       }, {
-               .name = "dra7-cpsw",
-               .driver_data = 0,
-       }, {
-               /* sentinel */
-       }
-};
-MODULE_DEVICE_TABLE(platform, cpsw_devtype);
-
-enum ti_cpsw_type {
-       CPSW = 0,
-       AM335X_CPSW,
-       AM4372_CPSW,
-       DRA7_CPSW,
-};
-
 static const struct of_device_id cpsw_of_mtable[] = {
-       { .compatible = "ti,cpsw", .data = &cpsw_devtype[CPSW], },
-       { .compatible = "ti,am335x-cpsw", .data = &cpsw_devtype[AM335X_CPSW], },
-       { .compatible = "ti,am4372-cpsw", .data = &cpsw_devtype[AM4372_CPSW], },
-       { .compatible = "ti,dra7-cpsw", .data = &cpsw_devtype[DRA7_CPSW], },
+       { .compatible = "ti,cpsw"},
+       { .compatible = "ti,am335x-cpsw"},
+       { .compatible = "ti,am4372-cpsw"},
+       { .compatible = "ti,dra7-cpsw"},
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, cpsw_of_mtable);
 
+static const struct soc_device_attribute cpsw_soc_devices[] = {
+       { .family = "AM33xx", .revision = "ES1.0"},
+       { /* sentinel */ }
+};
+
 static int cpsw_probe(struct platform_device *pdev)
 {
        struct clk                      *clk;
@@ -2966,9 +2976,9 @@ static int cpsw_probe(struct platform_device *pdev)
        void __iomem                    *ss_regs;
        void __iomem                    *cpts_regs;
        struct resource                 *res, *ss_res;
-       const struct of_device_id       *of_id;
        struct gpio_descs               *mode;
        u32 slave_offset, sliver_offset, slave_size;
+       const struct soc_device_attribute *soc;
        struct cpsw_common              *cpsw;
        int ret = 0, i;
        int irq;
@@ -3141,6 +3151,10 @@ static int cpsw_probe(struct platform_device *pdev)
                goto clean_dt_ret;
        }
 
+       soc = soc_device_match(cpsw_soc_devices);
+       if (soc)
+               cpsw->quirk_irq = 1;
+
        cpsw->txv[0].ch = cpdma_chan_create(cpsw->dma, 0, cpsw_tx_handler, 0);
        if (IS_ERR(cpsw->txv[0].ch)) {
                dev_err(priv->dev, "error initializing tx dma channel\n");
@@ -3180,19 +3194,16 @@ static int cpsw_probe(struct platform_device *pdev)
                goto clean_dma_ret;
        }
 
-       of_id = of_match_device(cpsw_of_mtable, &pdev->dev);
-       if (of_id) {
-               pdev->id_entry = of_id->data;
-               if (pdev->id_entry->driver_data)
-                       cpsw->quirk_irq = true;
-       }
-
        ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX;
 
        ndev->netdev_ops = &cpsw_netdev_ops;
        ndev->ethtool_ops = &cpsw_ethtool_ops;
-       netif_napi_add(ndev, &cpsw->napi_rx, cpsw_rx_poll, CPSW_POLL_WEIGHT);
-       netif_tx_napi_add(ndev, &cpsw->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT);
+       netif_napi_add(ndev, &cpsw->napi_rx,
+                      cpsw->quirk_irq ? cpsw_rx_poll : cpsw_rx_mq_poll,
+                      CPSW_POLL_WEIGHT);
+       netif_tx_napi_add(ndev, &cpsw->napi_tx,
+                         cpsw->quirk_irq ? cpsw_tx_poll : cpsw_tx_mq_poll,
+                         CPSW_POLL_WEIGHT);
        cpsw_split_res(ndev);
 
        /* register the network device */
index e7b76f6..6f63c87 100644 (file)
@@ -294,7 +294,8 @@ static long cpts_overflow_check(struct ptp_clock_info *ptp)
                delay = CPTS_SKB_TX_WORK_TIMEOUT;
        spin_unlock_irqrestore(&cpts->lock, flags);
 
-       pr_debug("cpts overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec);
+       pr_debug("cpts overflow check at %lld.%09ld\n",
+                (long long)ts.tv_sec, ts.tv_nsec);
        return (long)delay;
 }
 
@@ -564,7 +565,7 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
        cpts->refclk = devm_clk_get(dev, "cpts");
        if (IS_ERR(cpts->refclk)) {
                dev_err(dev, "Failed to get cpts refclk\n");
-               return ERR_PTR(PTR_ERR(cpts->refclk));
+               return ERR_CAST(cpts->refclk);
        }
 
        clk_prepare(cpts->refclk);
index 31ae041..cdbddf1 100644 (file)
@@ -191,7 +191,7 @@ static void cpdma_desc_pool_destroy(struct cpdma_ctlr *ctlr)
                return;
 
        WARN(gen_pool_size(pool->gen_pool) != gen_pool_avail(pool->gen_pool),
-            "cpdma_desc_pool size %d != avail %d",
+            "cpdma_desc_pool size %zd != avail %zd",
             gen_pool_size(pool->gen_pool),
             gen_pool_avail(pool->gen_pool));
        if (pool->cpumap)
@@ -1080,7 +1080,7 @@ int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
        writel_relaxed(buffer, &desc->hw_buffer);
        writel_relaxed(len, &desc->hw_len);
        writel_relaxed(mode | len, &desc->hw_mode);
-       writel_relaxed(token, &desc->sw_token);
+       writel_relaxed((uintptr_t)token, &desc->sw_token);
        writel_relaxed(buffer, &desc->sw_buffer);
        writel_relaxed(len, &desc->sw_len);
        desc_read(desc, sw_len);
@@ -1121,15 +1121,15 @@ static void __cpdma_chan_free(struct cpdma_chan *chan,
        struct cpdma_desc_pool          *pool = ctlr->pool;
        dma_addr_t                      buff_dma;
        int                             origlen;
-       void                            *token;
+       uintptr_t                       token;
 
-       token      = (void *)desc_read(desc, sw_token);
+       token      = desc_read(desc, sw_token);
        buff_dma   = desc_read(desc, sw_buffer);
        origlen    = desc_read(desc, sw_len);
 
        dma_unmap_single(ctlr->dev, buff_dma, origlen, chan->dir);
        cpdma_desc_free(pool, desc, 1);
-       (*chan->handler)(token, outlen, status);
+       (*chan->handler)((void *)token, outlen, status);
 }
 
 static int __cpdma_chan_process(struct cpdma_chan *chan)
index abceea8..be0fec1 100644 (file)
@@ -1930,8 +1930,8 @@ static int davinci_emac_probe(struct platform_device *pdev)
 
        if (netif_msg_probe(priv)) {
                dev_notice(&pdev->dev, "DaVinci EMAC Probe found device "
-                          "(regs: %p, irq: %d)\n",
-                          (void *)priv->emac_base_phys, ndev->irq);
+                          "(regs: %pa, irq: %d)\n",
+                          &priv->emac_base_phys, ndev->irq);
        }
        pm_runtime_put(&pdev->dev);
 
index 98a1c97..8ac7283 100644 (file)
@@ -429,12 +429,10 @@ static int davinci_mdio_probe(struct platform_device *pdev)
         * defined to support backward compatibility with DTs which assume that
         * Davinci MDIO will always scan the bus for PHYs detection.
         */
-       if (dev->of_node && of_get_child_count(dev->of_node)) {
+       if (dev->of_node && of_get_child_count(dev->of_node))
                data->skip_scan = true;
-               ret = of_mdiobus_register(data->bus, dev->of_node);
-       } else {
-               ret = mdiobus_register(data->bus);
-       }
+
+       ret = of_mdiobus_register(data->bus, dev->of_node);
        if (ret)
                goto bail_out;
 
index 1ab97d9..f411164 100644 (file)
@@ -867,7 +867,7 @@ static u32 rr_handle_event(struct net_device *dev, u32 prodidx, u32 eidx)
                               dev->name);
                        goto drop;
                case E_FRM_ERR:
-                       printk(KERN_WARNING "%s: Framming Error\n",
+                       printk(KERN_WARNING "%s: Framing Error\n",
                               dev->name);
                        goto drop;
                case E_FLG_SYN_ERR:
index da07ccd..60a5769 100644 (file)
@@ -1618,8 +1618,24 @@ static int netvsc_set_ringparam(struct net_device *ndev,
        return ret;
 }
 
+static u32 netvsc_get_msglevel(struct net_device *ndev)
+{
+       struct net_device_context *ndev_ctx = netdev_priv(ndev);
+
+       return ndev_ctx->msg_enable;
+}
+
+static void netvsc_set_msglevel(struct net_device *ndev, u32 val)
+{
+       struct net_device_context *ndev_ctx = netdev_priv(ndev);
+
+       ndev_ctx->msg_enable = val;
+}
+
 static const struct ethtool_ops ethtool_ops = {
        .get_drvinfo    = netvsc_get_drvinfo,
+       .get_msglevel   = netvsc_get_msglevel,
+       .set_msglevel   = netvsc_set_msglevel,
        .get_link       = ethtool_op_get_link,
        .get_ethtool_stats = netvsc_get_ethtool_stats,
        .get_sset_count = netvsc_get_sset_count,
index 450eec2..4377c26 100644 (file)
@@ -792,8 +792,10 @@ static int ipvlan_device_event(struct notifier_block *unused,
                break;
 
        case NETDEV_CHANGEADDR:
-               list_for_each_entry(ipvlan, &port->ipvlans, pnode)
+               list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
                        ether_addr_copy(ipvlan->dev->dev_addr, dev->dev_addr);
+                       call_netdevice_notifiers(NETDEV_CHANGEADDR, ipvlan->dev);
+               }
                break;
 
        case NETDEV_PRE_TYPE_CHANGE:
index 19a5778..343989f 100644 (file)
@@ -118,11 +118,18 @@ config MDIO_I2C
 
 config MDIO_MOXART
        tristate "MOXA ART MDIO interface support"
-       depends on ARCH_MOXART
+       depends on ARCH_MOXART || COMPILE_TEST
        help
          This driver supports the MDIO interface found in the network
          interface units of the MOXA ART SoC
 
+config MDIO_MSCC_MIIM
+       tristate "Microsemi MIIM interface support"
+       depends on HAS_IOMEM
+       help
+         This driver supports the MIIM (MDIO) interface found in the network
+         switches of the Microsemi SoCs
+
 config MDIO_OCTEON
        tristate "Octeon and some ThunderX SOCs MDIO buses"
        depends on 64BIT
@@ -135,7 +142,7 @@ config MDIO_OCTEON
 
 config MDIO_SUN4I
        tristate "Allwinner sun4i MDIO interface support"
-       depends on ARCH_SUNXI
+       depends on ARCH_SUNXI || COMPILE_TEST
        help
          This driver supports the MDIO interface found in the network
          interface units of the Allwinner SoC that have an EMAC (A10,
index 7382469..5805c0b 100644 (file)
@@ -34,6 +34,7 @@ obj-$(CONFIG_MDIO_GPIO)               += mdio-gpio.o
 obj-$(CONFIG_MDIO_HISI_FEMAC)  += mdio-hisi-femac.o
 obj-$(CONFIG_MDIO_I2C)         += mdio-i2c.o
 obj-$(CONFIG_MDIO_MOXART)      += mdio-moxart.o
+obj-$(CONFIG_MDIO_MSCC_MIIM)   += mdio-mscc-miim.o
 obj-$(CONFIG_MDIO_OCTEON)      += mdio-octeon.o
 obj-$(CONFIG_MDIO_SUN4I)       += mdio-sun4i.o
 obj-$(CONFIG_MDIO_THUNDER)     += mdio-thunder.o
index b501221..4e4c8da 100644 (file)
@@ -179,11 +179,7 @@ static int mdio_gpio_probe(struct platform_device *pdev)
        if (!new_bus)
                return -ENODEV;
 
-       if (pdev->dev.of_node)
-               ret = of_mdiobus_register(new_bus, pdev->dev.of_node);
-       else
-               ret = mdiobus_register(new_bus);
-
+       ret = of_mdiobus_register(new_bus, pdev->dev.of_node);
        if (ret)
                mdio_gpio_bus_deinit(&pdev->dev);
 
diff --git a/drivers/net/phy/mdio-mscc-miim.c b/drivers/net/phy/mdio-mscc-miim.c
new file mode 100644 (file)
index 0000000..badbc99
--- /dev/null
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Driver for the MDIO interface of Microsemi network switches.
+ *
+ * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/of_mdio.h>
+
+#define MSCC_MIIM_REG_STATUS           0x0
+#define                MSCC_MIIM_STATUS_STAT_BUSY      BIT(3)
+#define MSCC_MIIM_REG_CMD              0x8
+#define                MSCC_MIIM_CMD_OPR_WRITE         BIT(1)
+#define                MSCC_MIIM_CMD_OPR_READ          BIT(2)
+#define                MSCC_MIIM_CMD_WRDATA_SHIFT      4
+#define                MSCC_MIIM_CMD_REGAD_SHIFT       20
+#define                MSCC_MIIM_CMD_PHYAD_SHIFT       25
+#define                MSCC_MIIM_CMD_VLD               BIT(31)
+#define MSCC_MIIM_REG_DATA             0xC
+#define                MSCC_MIIM_DATA_ERROR            (BIT(16) | BIT(17))
+
+#define MSCC_PHY_REG_PHY_CFG   0x0
+#define                PHY_CFG_PHY_ENA         (BIT(0) | BIT(1) | BIT(2) | BIT(3))
+#define                PHY_CFG_PHY_COMMON_RESET BIT(4)
+#define                PHY_CFG_PHY_RESET       (BIT(5) | BIT(6) | BIT(7) | BIT(8))
+#define MSCC_PHY_REG_PHY_STATUS        0x4
+
+struct mscc_miim_dev {
+       void __iomem *regs;
+       void __iomem *phy_regs;
+};
+
+static int mscc_miim_wait_ready(struct mii_bus *bus)
+{
+       struct mscc_miim_dev *miim = bus->priv;
+       u32 val;
+
+       readl_poll_timeout(miim->regs + MSCC_MIIM_REG_STATUS, val,
+                          !(val & MSCC_MIIM_STATUS_STAT_BUSY), 100, 250000);
+       if (val & MSCC_MIIM_STATUS_STAT_BUSY)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+       struct mscc_miim_dev *miim = bus->priv;
+       u32 val;
+       int ret;
+
+       ret = mscc_miim_wait_ready(bus);
+       if (ret)
+               goto out;
+
+       writel(MSCC_MIIM_CMD_VLD | (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
+              (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | MSCC_MIIM_CMD_OPR_READ,
+              miim->regs + MSCC_MIIM_REG_CMD);
+
+       ret = mscc_miim_wait_ready(bus);
+       if (ret)
+               goto out;
+
+       val = readl(miim->regs + MSCC_MIIM_REG_DATA);
+       if (val & MSCC_MIIM_DATA_ERROR) {
+               ret = -EIO;
+               goto out;
+       }
+
+       ret = val & 0xFFFF;
+out:
+       return ret;
+}
+
+static int mscc_miim_write(struct mii_bus *bus, int mii_id,
+                          int regnum, u16 value)
+{
+       struct mscc_miim_dev *miim = bus->priv;
+       int ret;
+
+       ret = mscc_miim_wait_ready(bus);
+       if (ret < 0)
+               goto out;
+
+       writel(MSCC_MIIM_CMD_VLD | (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
+              (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
+              (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
+              MSCC_MIIM_CMD_OPR_WRITE,
+              miim->regs + MSCC_MIIM_REG_CMD);
+
+out:
+       return ret;
+}
+
+static int mscc_miim_reset(struct mii_bus *bus)
+{
+       struct mscc_miim_dev *miim = bus->priv;
+
+       if (miim->phy_regs) {
+               writel(0, miim->phy_regs + MSCC_PHY_REG_PHY_CFG);
+               writel(0x1ff, miim->phy_regs + MSCC_PHY_REG_PHY_CFG);
+               mdelay(500);
+       }
+
+       return 0;
+}
+
+static int mscc_miim_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct mii_bus *bus;
+       struct mscc_miim_dev *dev;
+       int ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENODEV;
+
+       bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*dev));
+       if (!bus)
+               return -ENOMEM;
+
+       bus->name = "mscc_miim";
+       bus->read = mscc_miim_read;
+       bus->write = mscc_miim_write;
+       bus->reset = mscc_miim_reset;
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+       bus->parent = &pdev->dev;
+
+       dev = bus->priv;
+       dev->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(dev->regs)) {
+               dev_err(&pdev->dev, "Unable to map MIIM registers\n");
+               return PTR_ERR(dev->regs);
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (res) {
+               dev->phy_regs = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(dev->phy_regs)) {
+                       dev_err(&pdev->dev, "Unable to map internal phy registers\n");
+                       return PTR_ERR(dev->phy_regs);
+               }
+       }
+
+       ret = of_mdiobus_register(bus, pdev->dev.of_node);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, bus);
+
+       return 0;
+}
+
+static int mscc_miim_remove(struct platform_device *pdev)
+{
+       struct mii_bus *bus = platform_get_drvdata(pdev);
+
+       mdiobus_unregister(bus);
+
+       return 0;
+}
+
+static const struct of_device_id mscc_miim_match[] = {
+       { .compatible = "mscc,ocelot-miim" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mscc_miim_match);
+
+static struct platform_driver mscc_miim_driver = {
+       .probe = mscc_miim_probe,
+       .remove = mscc_miim_remove,
+       .driver = {
+               .name = "mscc-miim",
+               .of_match_table = mscc_miim_match,
+       },
+};
+
+module_platform_driver(mscc_miim_driver);
+
+MODULE_DESCRIPTION("Microsemi MIIM driver");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
+MODULE_LICENSE("Dual MIT/GPL");
index de31c51..3db06b4 100644 (file)
@@ -573,9 +573,40 @@ static int ksz9031_config_init(struct phy_device *phydev)
                ksz9031_of_load_skew_values(phydev, of_node,
                                MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
                                tx_data_skews, 4);
+
+               /* Silicon Errata Sheet (DS80000691D or DS80000692D):
+                * When the device links in the 1000BASE-T slave mode only,
+                * the optional 125MHz reference output clock (CLK125_NDO)
+                * has wide duty cycle variation.
+                *
+                * The optional CLK125_NDO clock does not meet the RGMII
+                * 45/55 percent (min/max) duty cycle requirement and therefore
+                * cannot be used directly by the MAC side for clocking
+                * applications that have setup/hold time requirements on
+                * rising and falling clock edges.
+                *
+                * Workaround:
+                * Force the phy to be the master to receive a stable clock
+                * which meets the duty cycle requirement.
+                */
+               if (of_property_read_bool(of_node, "micrel,force-master")) {
+                       result = phy_read(phydev, MII_CTRL1000);
+                       if (result < 0)
+                               goto err_force_master;
+
+                       /* enable master mode, config & prefer master */
+                       result |= CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER;
+                       result = phy_write(phydev, MII_CTRL1000, result);
+                       if (result < 0)
+                               goto err_force_master;
+               }
        }
 
        return ksz9031_center_flp_timing(phydev);
+
+err_force_master:
+       phydev_err(phydev, "failed to force the phy to master mode\n");
+       return result;
 }
 
 #define KSZ8873MLL_GLOBAL_CONTROL_4    0x06
index 581ce93..af4dc44 100644 (file)
@@ -624,7 +624,7 @@ void phylink_destroy(struct phylink *pl)
 {
        if (pl->sfp_bus)
                sfp_unregister_upstream(pl->sfp_bus);
-       if (!IS_ERR(pl->link_gpio))
+       if (!IS_ERR_OR_NULL(pl->link_gpio))
                gpiod_put(pl->link_gpio);
 
        cancel_work_sync(&pl->resolve);
index 4ab6e9a..c4c92db 100644 (file)
@@ -976,6 +976,7 @@ static int sfp_probe(struct platform_device *pdev)
        if (pdev->dev.of_node) {
                struct device_node *node = pdev->dev.of_node;
                const struct of_device_id *id;
+               struct i2c_adapter *i2c;
                struct device_node *np;
 
                id = of_match_node(sfp_of_match, node);
@@ -985,19 +986,20 @@ static int sfp_probe(struct platform_device *pdev)
                sff = sfp->type = id->data;
 
                np = of_parse_phandle(node, "i2c-bus", 0);
-               if (np) {
-                       struct i2c_adapter *i2c;
-
-                       i2c = of_find_i2c_adapter_by_node(np);
-                       of_node_put(np);
-                       if (!i2c)
-                               return -EPROBE_DEFER;
-
-                       err = sfp_i2c_configure(sfp, i2c);
-                       if (err < 0) {
-                               i2c_put_adapter(i2c);
-                               return err;
-                       }
+               if (!np) {
+                       dev_err(sfp->dev, "missing 'i2c-bus' property\n");
+                       return -ENODEV;
+               }
+
+               i2c = of_find_i2c_adapter_by_node(np);
+               of_node_put(np);
+               if (!i2c)
+                       return -EPROBE_DEFER;
+
+               err = sfp_i2c_configure(sfp, i2c);
+               if (err < 0) {
+                       i2c_put_adapter(i2c);
+                       return err;
                }
        }
 
@@ -1065,6 +1067,15 @@ static int sfp_probe(struct platform_device *pdev)
        if (poll)
                mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
 
+       /* We could have an issue in cases no Tx disable pin is available or
+        * wired as modules using a laser as their light source will continue to
+        * be active when the fiber is removed. This could be a safety issue and
+        * we should at least warn the user about that.
+        */
+       if (!sfp->gpio[GPIO_TX_DISABLE])
+               dev_warn(sfp->dev,
+                        "No tx_disable pin: SFP modules will always be emitting.\n");
+
        return 0;
 }
 
index 9dbd390..d6ff881 100644 (file)
@@ -1026,7 +1026,8 @@ static void __team_compute_features(struct team *team)
        }
 
        team->dev->vlan_features = vlan_features;
-       team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL;
+       team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
+                                    NETIF_F_GSO_UDP_L4;
        team->dev->hard_header_len = max_hard_header_len;
 
        team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
@@ -2117,7 +2118,7 @@ static void team_setup(struct net_device *dev)
                           NETIF_F_HW_VLAN_CTAG_RX |
                           NETIF_F_HW_VLAN_CTAG_FILTER;
 
-       dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
+       dev->hw_features |= NETIF_F_GSO_ENCAP_ALL | NETIF_F_GSO_UDP_L4;
        dev->features |= dev->hw_features;
 }
 
index 44d4f3d..99bf3ce 100644 (file)
@@ -675,15 +675,6 @@ static void tun_queue_purge(struct tun_file *tfile)
        skb_queue_purge(&tfile->sk.sk_error_queue);
 }
 
-static void tun_cleanup_tx_ring(struct tun_file *tfile)
-{
-       if (tfile->tx_ring.queue) {
-               ptr_ring_cleanup(&tfile->tx_ring, tun_ptr_free);
-               xdp_rxq_info_unreg(&tfile->xdp_rxq);
-               memset(&tfile->tx_ring, 0, sizeof(tfile->tx_ring));
-       }
-}
-
 static void __tun_detach(struct tun_file *tfile, bool clean)
 {
        struct tun_file *ntfile;
@@ -730,7 +721,9 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
                            tun->dev->reg_state == NETREG_REGISTERED)
                                unregister_netdevice(tun->dev);
                }
-               tun_cleanup_tx_ring(tfile);
+               if (tun)
+                       xdp_rxq_info_unreg(&tfile->xdp_rxq);
+               ptr_ring_cleanup(&tfile->tx_ring, tun_ptr_free);
                sock_put(&tfile->sk);
        }
 }
@@ -777,14 +770,14 @@ static void tun_detach_all(struct net_device *dev)
                tun_napi_del(tun, tfile);
                /* Drop read queue */
                tun_queue_purge(tfile);
+               xdp_rxq_info_unreg(&tfile->xdp_rxq);
                sock_put(&tfile->sk);
-               tun_cleanup_tx_ring(tfile);
        }
        list_for_each_entry_safe(tfile, tmp, &tun->disabled, next) {
                tun_enable_queue(tfile);
                tun_queue_purge(tfile);
+               xdp_rxq_info_unreg(&tfile->xdp_rxq);
                sock_put(&tfile->sk);
-               tun_cleanup_tx_ring(tfile);
        }
        BUG_ON(tun->numdisabled != 0);
 
@@ -828,7 +821,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file,
        }
 
        if (!tfile->detached &&
-           ptr_ring_init(&tfile->tx_ring, dev->tx_queue_len, GFP_KERNEL)) {
+           ptr_ring_resize(&tfile->tx_ring, dev->tx_queue_len,
+                           GFP_KERNEL, tun_ptr_free)) {
                err = -ENOMEM;
                goto out;
        }
@@ -3221,6 +3215,11 @@ static int tun_chr_open(struct inode *inode, struct file * file)
                                            &tun_proto, 0);
        if (!tfile)
                return -ENOMEM;
+       if (ptr_ring_init(&tfile->tx_ring, 0, GFP_KERNEL)) {
+               sk_free(&tfile->sk);
+               return -ENOMEM;
+       }
+
        RCU_INIT_POINTER(tfile->tun, NULL);
        tfile->flags = 0;
        tfile->ifindex = 0;
@@ -3241,8 +3240,6 @@ static int tun_chr_open(struct inode *inode, struct file * file)
 
        sock_set_flag(&tfile->sk, SOCK_ZEROCOPY);
 
-       memset(&tfile->tx_ring, 0, sizeof(tfile->tx_ring));
-
        return 0;
 }
 
index 9176143..8dff87e 100644 (file)
@@ -1843,12 +1843,9 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev)
        }
 
        node = of_get_child_by_name(dev->udev->dev.of_node, "mdio");
-       if (node) {
-               ret = of_mdiobus_register(dev->mdiobus, node);
+       ret = of_mdiobus_register(dev->mdiobus, node);
+       if (node)
                of_node_put(node);
-       } else {
-               ret = mdiobus_register(dev->mdiobus);
-       }
        if (ret) {
                netdev_err(dev->net, "can't register MDIO bus\n");
                goto exit1;
index 9ebe2a6..e454dfc 100644 (file)
@@ -369,6 +369,11 @@ vmxnet3_tq_tx_complete(struct vmxnet3_tx_queue *tq,
 
        gdesc = tq->comp_ring.base + tq->comp_ring.next2proc;
        while (VMXNET3_TCD_GET_GEN(&gdesc->tcd) == tq->comp_ring.gen) {
+               /* Prevent any &gdesc->tcd field from being (speculatively)
+                * read before (&gdesc->tcd)->gen is read.
+                */
+               dma_rmb();
+
                completed += vmxnet3_unmap_pkt(VMXNET3_TCD_GET_TXIDX(
                                               &gdesc->tcd), tq, adapter->pdev,
                                               adapter);
@@ -1103,6 +1108,11 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
                gdesc->txd.tci = skb_vlan_tag_get(skb);
        }
 
+       /* Ensure that the write to (&gdesc->txd)->gen will be observed after
+        * all other writes to &gdesc->txd.
+        */
+       dma_wmb();
+
        /* finally flips the GEN bit of the SOP desc. */
        gdesc->dword[2] = cpu_to_le32(le32_to_cpu(gdesc->dword[2]) ^
                                                  VMXNET3_TXD_GEN);
@@ -1298,6 +1308,12 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
                         */
                        break;
                }
+
+               /* Prevent any rcd field from being (speculatively) read before
+                * rcd->gen is read.
+                */
+               dma_rmb();
+
                BUG_ON(rcd->rqID != rq->qid && rcd->rqID != rq->qid2 &&
                       rcd->rqID != rq->dataRingQid);
                idx = rcd->rxdIdx;
@@ -1528,6 +1544,12 @@ rcd_done:
                ring->next2comp = idx;
                num_to_alloc = vmxnet3_cmd_ring_desc_avail(ring);
                ring = rq->rx_ring + ring_idx;
+
+               /* Ensure that the writes to rxd->gen bits will be observed
+                * after all other writes to rxd objects.
+                */
+               dma_wmb();
+
                while (num_to_alloc) {
                        vmxnet3_getRxDesc(rxd, &ring->base[ring->next2fill].rxd,
                                          &rxCmdDesc);
@@ -2688,7 +2710,7 @@ vmxnet3_set_mac_addr(struct net_device *netdev, void *p)
 /* ==================== initialization and cleanup routines ============ */
 
 static int
-vmxnet3_alloc_pci_resources(struct vmxnet3_adapter *adapter, bool *dma64)
+vmxnet3_alloc_pci_resources(struct vmxnet3_adapter *adapter)
 {
        int err;
        unsigned long mmio_start, mmio_len;
@@ -2700,30 +2722,12 @@ vmxnet3_alloc_pci_resources(struct vmxnet3_adapter *adapter, bool *dma64)
                return err;
        }
 
-       if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) {
-               if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) {
-                       dev_err(&pdev->dev,
-                               "pci_set_consistent_dma_mask failed\n");
-                       err = -EIO;
-                       goto err_set_mask;
-               }
-               *dma64 = true;
-       } else {
-               if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
-                       dev_err(&pdev->dev,
-                               "pci_set_dma_mask failed\n");
-                       err = -EIO;
-                       goto err_set_mask;
-               }
-               *dma64 = false;
-       }
-
        err = pci_request_selected_regions(pdev, (1 << 2) - 1,
                                           vmxnet3_driver_name);
        if (err) {
                dev_err(&pdev->dev,
                        "Failed to request region for adapter: error %d\n", err);
-               goto err_set_mask;
+               goto err_enable_device;
        }
 
        pci_set_master(pdev);
@@ -2751,7 +2755,7 @@ err_bar1:
        iounmap(adapter->hw_addr0);
 err_ioremap:
        pci_release_selected_regions(pdev, (1 << 2) - 1);
-err_set_mask:
+err_enable_device:
        pci_disable_device(pdev);
        return err;
 }
@@ -2945,7 +2949,7 @@ vmxnet3_close(struct net_device *netdev)
         * completion.
         */
        while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        vmxnet3_quiesce_dev(adapter);
 
@@ -2995,7 +2999,7 @@ vmxnet3_change_mtu(struct net_device *netdev, int new_mtu)
         * completion.
         */
        while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        if (netif_running(netdev)) {
                vmxnet3_quiesce_dev(adapter);
@@ -3254,7 +3258,7 @@ vmxnet3_probe_device(struct pci_dev *pdev,
 #endif
        };
        int err;
-       bool dma64 = false; /* stupid gcc */
+       bool dma64;
        u32 ver;
        struct net_device *netdev;
        struct vmxnet3_adapter *adapter;
@@ -3300,6 +3304,24 @@ vmxnet3_probe_device(struct pci_dev *pdev,
        adapter->rx_ring_size = VMXNET3_DEF_RX_RING_SIZE;
        adapter->rx_ring2_size = VMXNET3_DEF_RX_RING2_SIZE;
 
+       if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) {
+               if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) {
+                       dev_err(&pdev->dev,
+                               "pci_set_consistent_dma_mask failed\n");
+                       err = -EIO;
+                       goto err_set_mask;
+               }
+               dma64 = true;
+       } else {
+               if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
+                       dev_err(&pdev->dev,
+                               "pci_set_dma_mask failed\n");
+                       err = -EIO;
+                       goto err_set_mask;
+               }
+               dma64 = false;
+       }
+
        spin_lock_init(&adapter->cmd_lock);
        adapter->adapter_pa = dma_map_single(&adapter->pdev->dev, adapter,
                                             sizeof(struct vmxnet3_adapter),
@@ -3307,7 +3329,7 @@ vmxnet3_probe_device(struct pci_dev *pdev,
        if (dma_mapping_error(&adapter->pdev->dev, adapter->adapter_pa)) {
                dev_err(&pdev->dev, "Failed to map dma\n");
                err = -EFAULT;
-               goto err_dma_map;
+               goto err_set_mask;
        }
        adapter->shared = dma_alloc_coherent(
                                &adapter->pdev->dev,
@@ -3358,7 +3380,7 @@ vmxnet3_probe_device(struct pci_dev *pdev,
        }
 #endif /* VMXNET3_RSS */
 
-       err = vmxnet3_alloc_pci_resources(adapter, &dma64);
+       err = vmxnet3_alloc_pci_resources(adapter);
        if (err < 0)
                goto err_alloc_pci;
 
@@ -3504,7 +3526,7 @@ err_alloc_queue_desc:
 err_alloc_shared:
        dma_unmap_single(&adapter->pdev->dev, adapter->adapter_pa,
                         sizeof(struct vmxnet3_adapter), PCI_DMA_TODEVICE);
-err_dma_map:
+err_set_mask:
        free_netdev(netdev);
        return err;
 }
@@ -3567,7 +3589,7 @@ static void vmxnet3_shutdown_device(struct pci_dev *pdev)
         * completion.
         */
        while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        if (test_and_set_bit(VMXNET3_STATE_BIT_QUIESCED,
                             &adapter->state)) {
index 2ff2731..559db05 100644 (file)
@@ -600,7 +600,7 @@ vmxnet3_set_ringparam(struct net_device *netdev,
         * completion.
         */
        while (test_and_set_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        if (netif_running(netdev)) {
                vmxnet3_quiesce_dev(adapter);
index a332646..a2c554f 100644 (file)
 /*
  * Version numbers
  */
-#define VMXNET3_DRIVER_VERSION_STRING   "1.4.14.0-k"
+#define VMXNET3_DRIVER_VERSION_STRING   "1.4.16.0-k"
 
-/* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */
-#define VMXNET3_DRIVER_VERSION_NUM      0x01040e00
+/* Each byte of this 32-bit integer encodes a version number in
+ * VMXNET3_DRIVER_VERSION_STRING.
+ */
+#define VMXNET3_DRIVER_VERSION_NUM      0x01041000
 
 #if defined(CONFIG_PCI_MSI)
        /* RSS only makes sense if MSI-X is supported. */
index deb5ae2..84f071a 100644 (file)
@@ -4,12 +4,16 @@ config ATH10K
        select ATH_COMMON
        select CRC32
        select WANT_DEV_COREDUMP
+       select ATH10K_CE
         ---help---
           This module adds support for wireless adapters based on
           Atheros IEEE 802.11ac family of chipsets.
 
           If you choose to build a module, it'll be called ath10k.
 
+config ATH10K_CE
+       bool
+
 config ATH10K_PCI
        tristate "Atheros ath10k PCI support"
        depends on ATH10K && PCI
@@ -36,6 +40,14 @@ config ATH10K_USB
          This module adds experimental support for USB bus. Currently
          work in progress and will not fully work.
 
+config ATH10K_SNOC
+        tristate "Qualcomm ath10k SNOC support (EXPERIMENTAL)"
+        depends on ATH10K && ARCH_QCOM
+        ---help---
+          This module adds support for integrated WCN3990 chip connected
+          to system NOC(SNOC). Currently work in progress and will not
+          fully work.
+
 config ATH10K_DEBUG
        bool "Atheros ath10k debugging"
        depends on ATH10K
index 6739ac2..44d60a6 100644 (file)
@@ -22,10 +22,10 @@ ath10k_core-$(CONFIG_THERMAL) += thermal.o
 ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o
 ath10k_core-$(CONFIG_PM) += wow.o
 ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o
+ath10k_core-$(CONFIG_ATH10K_CE) += ce.o
 
 obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o
-ath10k_pci-y += pci.o \
-               ce.o
+ath10k_pci-y += pci.o
 
 ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o
 
@@ -35,5 +35,8 @@ ath10k_sdio-y += sdio.o
 obj-$(CONFIG_ATH10K_USB) += ath10k_usb.o
 ath10k_usb-y += usb.o
 
+obj-$(CONFIG_ATH10K_SNOC) += ath10k_snoc.o
+ath10k_snoc-y += snoc.o
+
 # for tracing framework to find trace.h
 CFLAGS_trace.o := -I$(src)
index b9def7b..3b96a43 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * the buffer is sent/received.
  */
 
+static inline u32 shadow_sr_wr_ind_addr(struct ath10k *ar,
+                                       struct ath10k_ce_pipe *ce_state)
+{
+       u32 ce_id = ce_state->id;
+       u32 addr = 0;
+
+       switch (ce_id) {
+       case 0:
+               addr = 0x00032000;
+               break;
+       case 3:
+               addr = 0x0003200C;
+               break;
+       case 4:
+               addr = 0x00032010;
+               break;
+       case 5:
+               addr = 0x00032014;
+               break;
+       case 7:
+               addr = 0x0003201C;
+               break;
+       default:
+               ath10k_warn(ar, "invalid CE id: %d", ce_id);
+               break;
+       }
+       return addr;
+}
+
+static inline u32 shadow_dst_wr_ind_addr(struct ath10k *ar,
+                                        struct ath10k_ce_pipe *ce_state)
+{
+       u32 ce_id = ce_state->id;
+       u32 addr = 0;
+
+       switch (ce_id) {
+       case 1:
+               addr = 0x00032034;
+               break;
+       case 2:
+               addr = 0x00032038;
+               break;
+       case 5:
+               addr = 0x00032044;
+               break;
+       case 7:
+               addr = 0x0003204C;
+               break;
+       case 8:
+               addr = 0x00032050;
+               break;
+       case 9:
+               addr = 0x00032054;
+               break;
+       case 10:
+               addr = 0x00032058;
+               break;
+       case 11:
+               addr = 0x0003205C;
+               break;
+       default:
+               ath10k_warn(ar, "invalid CE id: %d", ce_id);
+               break;
+       }
+
+       return addr;
+}
+
 static inline unsigned int
 ath10k_set_ring_byte(unsigned int offset,
                     struct ath10k_hw_ce_regs_addr_map *addr_map)
@@ -116,11 +185,46 @@ static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar,
                                ar->hw_ce_regs->sr_wr_index_addr);
 }
 
+static inline u32 ath10k_ce_src_ring_read_index_from_ddr(struct ath10k *ar,
+                                                        u32 ce_id)
+{
+       struct ath10k_ce *ce = ath10k_ce_priv(ar);
+
+       return ce->vaddr_rri[ce_id] & CE_DDR_RRI_MASK;
+}
+
 static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar,
                                                    u32 ce_ctrl_addr)
 {
-       return ath10k_ce_read32(ar, ce_ctrl_addr +
-                               ar->hw_ce_regs->current_srri_addr);
+       struct ath10k_ce *ce = ath10k_ce_priv(ar);
+       u32 ce_id = COPY_ENGINE_ID(ce_ctrl_addr);
+       struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id];
+       u32 index;
+
+       if (ar->hw_params.rri_on_ddr &&
+           (ce_state->attr_flags & CE_ATTR_DIS_INTR))
+               index = ath10k_ce_src_ring_read_index_from_ddr(ar, ce_id);
+       else
+               index = ath10k_ce_read32(ar, ce_ctrl_addr +
+                                        ar->hw_ce_regs->current_srri_addr);
+
+       return index;
+}
+
+static inline void
+ath10k_ce_shadow_src_ring_write_index_set(struct ath10k *ar,
+                                         struct ath10k_ce_pipe *ce_state,
+                                         unsigned int value)
+{
+       ath10k_ce_write32(ar, shadow_sr_wr_ind_addr(ar, ce_state), value);
+}
+
+static inline void
+ath10k_ce_shadow_dest_ring_write_index_set(struct ath10k *ar,
+                                          struct ath10k_ce_pipe *ce_state,
+                                          unsigned int value)
+{
+       ath10k_ce_write32(ar, shadow_dst_wr_ind_addr(ar, ce_state), value);
 }
 
 static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar,
@@ -181,11 +285,31 @@ static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar,
                          ath10k_set_ring_byte(n, ctrl_regs->dst_ring));
 }
 
+static inline
+       u32 ath10k_ce_dest_ring_read_index_from_ddr(struct ath10k *ar, u32 ce_id)
+{
+       struct ath10k_ce *ce = ath10k_ce_priv(ar);
+
+       return (ce->vaddr_rri[ce_id] >> CE_DDR_DRRI_SHIFT) &
+               CE_DDR_RRI_MASK;
+}
+
 static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar,
                                                     u32 ce_ctrl_addr)
 {
-       return ath10k_ce_read32(ar, ce_ctrl_addr +
-                               ar->hw_ce_regs->current_drri_addr);
+       struct ath10k_ce *ce = ath10k_ce_priv(ar);
+       u32 ce_id = COPY_ENGINE_ID(ce_ctrl_addr);
+       struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id];
+       u32 index;
+
+       if (ar->hw_params.rri_on_ddr &&
+           (ce_state->attr_flags & CE_ATTR_DIS_INTR))
+               index = ath10k_ce_dest_ring_read_index_from_ddr(ar, ce_id);
+       else
+               index = ath10k_ce_read32(ar, ce_ctrl_addr +
+                                        ar->hw_ce_regs->current_drri_addr);
+
+       return index;
 }
 
 static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar,
@@ -376,8 +500,14 @@ static int _ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
        write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
 
        /* WORKAROUND */
-       if (!(flags & CE_SEND_FLAG_GATHER))
-               ath10k_ce_src_ring_write_index_set(ar, ctrl_addr, write_index);
+       if (!(flags & CE_SEND_FLAG_GATHER)) {
+               if (ar->hw_params.shadow_reg_support)
+                       ath10k_ce_shadow_src_ring_write_index_set(ar, ce_state,
+                                                                 write_index);
+               else
+                       ath10k_ce_src_ring_write_index_set(ar, ctrl_addr,
+                                                          write_index);
+       }
 
        src_ring->write_index = write_index;
 exit:
@@ -395,7 +525,7 @@ static int _ath10k_ce_send_nolock_64(struct ath10k_ce_pipe *ce_state,
        struct ath10k_ce_ring *src_ring = ce_state->src_ring;
        struct ce_desc_64 *desc, sdesc;
        unsigned int nentries_mask = src_ring->nentries_mask;
-       unsigned int sw_index = src_ring->sw_index;
+       unsigned int sw_index;
        unsigned int write_index = src_ring->write_index;
        u32 ctrl_addr = ce_state->ctrl_addr;
        __le32 *addr;
@@ -409,6 +539,11 @@ static int _ath10k_ce_send_nolock_64(struct ath10k_ce_pipe *ce_state,
                ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n",
                            __func__, nbytes, ce_state->src_sz_max);
 
+       if (ar->hw_params.rri_on_ddr)
+               sw_index = ath10k_ce_src_ring_read_index_from_ddr(ar, ce_state->id);
+       else
+               sw_index = src_ring->sw_index;
+
        if (unlikely(CE_RING_DELTA(nentries_mask,
                                   write_index, sw_index - 1) <= 0)) {
                ret = -ENOSR;
@@ -464,6 +599,7 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
        return ce_state->ops->ce_send_nolock(ce_state, per_transfer_context,
                                    buffer, nbytes, transfer_id, flags);
 }
+EXPORT_SYMBOL(ath10k_ce_send_nolock);
 
 void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe)
 {
@@ -491,6 +627,7 @@ void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe)
 
        src_ring->per_transfer_context[src_ring->write_index] = NULL;
 }
+EXPORT_SYMBOL(__ath10k_ce_send_revert);
 
 int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
                   void *per_transfer_context,
@@ -510,6 +647,7 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
 
        return ret;
 }
+EXPORT_SYMBOL(ath10k_ce_send);
 
 int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe)
 {
@@ -525,6 +663,7 @@ int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe)
 
        return delta;
 }
+EXPORT_SYMBOL(ath10k_ce_num_free_src_entries);
 
 int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe)
 {
@@ -539,6 +678,7 @@ int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe)
 
        return CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
 }
+EXPORT_SYMBOL(__ath10k_ce_rx_num_free_bufs);
 
 static int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx,
                                   dma_addr_t paddr)
@@ -615,13 +755,14 @@ void ath10k_ce_rx_update_write_idx(struct ath10k_ce_pipe *pipe, u32 nentries)
        /* Prevent CE ring stuck issue that will occur when ring is full.
         * Make sure that write index is 1 less than read index.
         */
-       if ((cur_write_idx + nentries)  == dest_ring->sw_index)
+       if (((cur_write_idx + nentries) & nentries_mask) == dest_ring->sw_index)
                nentries -= 1;
 
        write_index = CE_RING_IDX_ADD(nentries_mask, write_index, nentries);
        ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index);
        dest_ring->write_index = write_index;
 }
+EXPORT_SYMBOL(ath10k_ce_rx_update_write_idx);
 
 int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx,
                          dma_addr_t paddr)
@@ -636,6 +777,7 @@ int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx,
 
        return ret;
 }
+EXPORT_SYMBOL(ath10k_ce_rx_post_buf);
 
 /*
  * Guts of ath10k_ce_completed_recv_next.
@@ -748,6 +890,7 @@ int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
                                                            per_transfer_ctx,
                                                            nbytesp);
 }
+EXPORT_SYMBOL(ath10k_ce_completed_recv_next_nolock);
 
 int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
                                  void **per_transfer_contextp,
@@ -766,6 +909,7 @@ int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
 
        return ret;
 }
+EXPORT_SYMBOL(ath10k_ce_completed_recv_next);
 
 static int _ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
                                       void **per_transfer_contextp,
@@ -882,6 +1026,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
                                                  per_transfer_contextp,
                                                  bufferp);
 }
+EXPORT_SYMBOL(ath10k_ce_revoke_recv_next);
 
 /*
  * Guts of ath10k_ce_completed_send_next.
@@ -915,7 +1060,10 @@ int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
                src_ring->hw_index = read_index;
        }
 
-       read_index = src_ring->hw_index;
+       if (ar->hw_params.rri_on_ddr)
+               read_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+       else
+               read_index = src_ring->hw_index;
 
        if (read_index == sw_index)
                return -EIO;
@@ -936,6 +1084,7 @@ int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
 
        return 0;
 }
+EXPORT_SYMBOL(ath10k_ce_completed_send_next_nolock);
 
 static void ath10k_ce_extract_desc_data(struct ath10k *ar,
                                        struct ath10k_ce_ring *src_ring,
@@ -1025,6 +1174,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
 
        return ret;
 }
+EXPORT_SYMBOL(ath10k_ce_cancel_send_next);
 
 int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
                                  void **per_transfer_contextp)
@@ -1040,6 +1190,7 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
 
        return ret;
 }
+EXPORT_SYMBOL(ath10k_ce_completed_send_next);
 
 /*
  * Guts of interrupt handler for per-engine interrupts on a particular CE.
@@ -1078,6 +1229,7 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
 
        spin_unlock_bh(&ce->ce_lock);
 }
+EXPORT_SYMBOL(ath10k_ce_per_engine_service);
 
 /*
  * Handler for per-engine interrupts on ALL active CEs.
@@ -1102,6 +1254,7 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar)
                ath10k_ce_per_engine_service(ar, ce_id);
        }
 }
+EXPORT_SYMBOL(ath10k_ce_per_engine_service_any);
 
 /*
  * Adjust interrupts for the copy complete handler.
@@ -1139,6 +1292,7 @@ int ath10k_ce_disable_interrupts(struct ath10k *ar)
 
        return 0;
 }
+EXPORT_SYMBOL(ath10k_ce_disable_interrupts);
 
 void ath10k_ce_enable_interrupts(struct ath10k *ar)
 {
@@ -1154,6 +1308,7 @@ void ath10k_ce_enable_interrupts(struct ath10k *ar)
                ath10k_ce_per_engine_handler_adjust(ce_state);
        }
 }
+EXPORT_SYMBOL(ath10k_ce_enable_interrupts);
 
 static int ath10k_ce_init_src_ring(struct ath10k *ar,
                                   unsigned int ce_id,
@@ -1234,6 +1389,22 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
        return 0;
 }
 
+static int ath10k_ce_alloc_shadow_base(struct ath10k *ar,
+                                      struct ath10k_ce_ring *src_ring,
+                                      u32 nentries)
+{
+       src_ring->shadow_base_unaligned = kcalloc(nentries,
+                                                 sizeof(struct ce_desc),
+                                                 GFP_KERNEL);
+       if (!src_ring->shadow_base_unaligned)
+               return -ENOMEM;
+
+       src_ring->shadow_base = (struct ce_desc *)
+                       PTR_ALIGN(src_ring->shadow_base_unaligned,
+                                 CE_DESC_RING_ALIGN);
+       return 0;
+}
+
 static struct ath10k_ce_ring *
 ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id,
                         const struct ce_attr *attr)
@@ -1241,6 +1412,7 @@ ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id,
        struct ath10k_ce_ring *src_ring;
        u32 nentries = attr->src_nentries;
        dma_addr_t base_addr;
+       int ret;
 
        nentries = roundup_pow_of_two(nentries);
 
@@ -1277,6 +1449,19 @@ ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id,
                        ALIGN(src_ring->base_addr_ce_space_unaligned,
                              CE_DESC_RING_ALIGN);
 
+       if (ar->hw_params.shadow_reg_support) {
+               ret = ath10k_ce_alloc_shadow_base(ar, src_ring, nentries);
+               if (ret) {
+                       dma_free_coherent(ar->dev,
+                                         (nentries * sizeof(struct ce_desc) +
+                                          CE_DESC_RING_ALIGN),
+                                         src_ring->base_addr_owner_space_unaligned,
+                                         base_addr);
+                       kfree(src_ring);
+                       return ERR_PTR(ret);
+               }
+       }
+
        return src_ring;
 }
 
@@ -1287,6 +1472,7 @@ ath10k_ce_alloc_src_ring_64(struct ath10k *ar, unsigned int ce_id,
        struct ath10k_ce_ring *src_ring;
        u32 nentries = attr->src_nentries;
        dma_addr_t base_addr;
+       int ret;
 
        nentries = roundup_pow_of_two(nentries);
 
@@ -1322,6 +1508,19 @@ ath10k_ce_alloc_src_ring_64(struct ath10k *ar, unsigned int ce_id,
                        ALIGN(src_ring->base_addr_ce_space_unaligned,
                              CE_DESC_RING_ALIGN);
 
+       if (ar->hw_params.shadow_reg_support) {
+               ret = ath10k_ce_alloc_shadow_base(ar, src_ring, nentries);
+               if (ret) {
+                       dma_free_coherent(ar->dev,
+                                         (nentries * sizeof(struct ce_desc) +
+                                          CE_DESC_RING_ALIGN),
+                                         src_ring->base_addr_owner_space_unaligned,
+                                         base_addr);
+                       kfree(src_ring);
+                       return ERR_PTR(ret);
+               }
+       }
+
        return src_ring;
 }
 
@@ -1454,6 +1653,7 @@ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
 
        return 0;
 }
+EXPORT_SYMBOL(ath10k_ce_init_pipe);
 
 static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id)
 {
@@ -1479,6 +1679,7 @@ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id)
        ath10k_ce_deinit_src_ring(ar, ce_id);
        ath10k_ce_deinit_dest_ring(ar, ce_id);
 }
+EXPORT_SYMBOL(ath10k_ce_deinit_pipe);
 
 static void _ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
 {
@@ -1486,6 +1687,8 @@ static void _ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
        struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id];
 
        if (ce_state->src_ring) {
+               if (ar->hw_params.shadow_reg_support)
+                       kfree(ce_state->src_ring->shadow_base_unaligned);
                dma_free_coherent(ar->dev,
                                  (ce_state->src_ring->nentries *
                                   sizeof(struct ce_desc) +
@@ -1515,6 +1718,8 @@ static void _ath10k_ce_free_pipe_64(struct ath10k *ar, int ce_id)
        struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id];
 
        if (ce_state->src_ring) {
+               if (ar->hw_params.shadow_reg_support)
+                       kfree(ce_state->src_ring->shadow_base_unaligned);
                dma_free_coherent(ar->dev,
                                  (ce_state->src_ring->nentries *
                                   sizeof(struct ce_desc_64) +
@@ -1545,6 +1750,7 @@ void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
 
        ce_state->ops->ce_free_pipe(ar, ce_id);
 }
+EXPORT_SYMBOL(ath10k_ce_free_pipe);
 
 void ath10k_ce_dump_registers(struct ath10k *ar,
                              struct ath10k_fw_crash_data *crash_data)
@@ -1584,6 +1790,7 @@ void ath10k_ce_dump_registers(struct ath10k *ar,
 
        spin_unlock_bh(&ce->ce_lock);
 }
+EXPORT_SYMBOL(ath10k_ce_dump_registers);
 
 static const struct ath10k_ce_ops ce_ops = {
        .ce_alloc_src_ring = ath10k_ce_alloc_src_ring,
@@ -1680,3 +1887,47 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
 
        return 0;
 }
+EXPORT_SYMBOL(ath10k_ce_alloc_pipe);
+
+void ath10k_ce_alloc_rri(struct ath10k *ar)
+{
+       int i;
+       u32 value;
+       u32 ctrl1_regs;
+       u32 ce_base_addr;
+       struct ath10k_ce *ce = ath10k_ce_priv(ar);
+
+       ce->vaddr_rri = dma_alloc_coherent(ar->dev,
+                                          (CE_COUNT * sizeof(u32)),
+                                          &ce->paddr_rri, GFP_KERNEL);
+
+       if (!ce->vaddr_rri)
+               return;
+
+       ath10k_ce_write32(ar, ar->hw_ce_regs->ce_rri_low,
+                         lower_32_bits(ce->paddr_rri));
+       ath10k_ce_write32(ar, ar->hw_ce_regs->ce_rri_high,
+                         (upper_32_bits(ce->paddr_rri) &
+                         CE_DESC_FLAGS_GET_MASK));
+
+       for (i = 0; i < CE_COUNT; i++) {
+               ctrl1_regs = ar->hw_ce_regs->ctrl1_regs->addr;
+               ce_base_addr = ath10k_ce_base_address(ar, i);
+               value = ath10k_ce_read32(ar, ce_base_addr + ctrl1_regs);
+               value |= ar->hw_ce_regs->upd->mask;
+               ath10k_ce_write32(ar, ce_base_addr + ctrl1_regs, value);
+       }
+
+       memset(ce->vaddr_rri, 0, CE_COUNT * sizeof(u32));
+}
+EXPORT_SYMBOL(ath10k_ce_alloc_rri);
+
+void ath10k_ce_free_rri(struct ath10k *ar)
+{
+       struct ath10k_ce *ce = ath10k_ce_priv(ar);
+
+       dma_free_coherent(ar->dev, (CE_COUNT * sizeof(u32)),
+                         ce->vaddr_rri,
+                         ce->paddr_rri);
+}
+EXPORT_SYMBOL(ath10k_ce_free_rri);
index 2c3c8f5..dbeffae 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -48,6 +49,9 @@ struct ath10k_ce_pipe;
 #define CE_DESC_FLAGS_META_DATA_MASK ar->hw_values->ce_desc_meta_data_mask
 #define CE_DESC_FLAGS_META_DATA_LSB  ar->hw_values->ce_desc_meta_data_lsb
 
+#define CE_DDR_RRI_MASK                        GENMASK(15, 0)
+#define CE_DDR_DRRI_SHIFT              16
+
 struct ce_desc {
        __le32 addr;
        __le16 nbytes;
@@ -113,6 +117,9 @@ struct ath10k_ce_ring {
        /* CE address space */
        u32 base_addr_ce_space;
 
+       char *shadow_base_unaligned;
+       struct ce_desc *shadow_base;
+
        /* keep last */
        void *per_transfer_context[0];
 };
@@ -153,6 +160,8 @@ struct ath10k_ce {
        spinlock_t ce_lock;
        const struct ath10k_bus_ops *bus_ops;
        struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
+       u32 *vaddr_rri;
+       dma_addr_t paddr_rri;
 };
 
 /*==================Send====================*/
@@ -261,6 +270,8 @@ int ath10k_ce_disable_interrupts(struct ath10k *ar);
 void ath10k_ce_enable_interrupts(struct ath10k *ar);
 void ath10k_ce_dump_registers(struct ath10k *ar,
                              struct ath10k_fw_crash_data *crash_data);
+void ath10k_ce_alloc_rri(struct ath10k *ar);
+void ath10k_ce_free_rri(struct ath10k *ar);
 
 /* ce_attr.flags values */
 /* Use NonSnooping PCIe accesses? */
@@ -327,6 +338,9 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
        return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
 }
 
+#define COPY_ENGINE_ID(COPY_ENGINE_BASE_ADDRESS) (((COPY_ENGINE_BASE_ADDRESS) \
+               - CE0_BASE_ADDRESS) / (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS))
+
 #define CE_SRC_RING_TO_DESC(baddr, idx) \
        (&(((struct ce_desc *)baddr)[idx]))
 
@@ -355,14 +369,18 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
        (((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \
                CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
 #define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS                   0x0000
+#define CE_INTERRUPT_SUMMARY           (GENMASK(CE_COUNT_MAX - 1, 0))
 
 static inline u32 ath10k_ce_interrupt_summary(struct ath10k *ar)
 {
        struct ath10k_ce *ce = ath10k_ce_priv(ar);
 
-       return CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(
-               ce->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS +
-               CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS));
+       if (!ar->hw_params.per_ce_irq)
+               return CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(
+                       ce->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS +
+                       CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS));
+       else
+               return CE_INTERRUPT_SUMMARY;
 }
 
 #endif /* _CE_H_ */
index 8a3020d..4cf54a7 100644 (file)
@@ -90,6 +90,8 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA988X_HW_2_0_VERSION,
@@ -119,6 +121,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA9887_HW_1_0_VERSION,
@@ -148,6 +153,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA6174_HW_2_1_VERSION,
@@ -176,6 +184,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA6174_HW_2_1_VERSION,
@@ -204,6 +215,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA6174_HW_3_0_VERSION,
@@ -232,6 +246,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA6174_HW_3_2_VERSION,
@@ -263,6 +280,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA99X0_HW_2_0_DEV_VERSION,
@@ -297,6 +317,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA9984_HW_1_0_DEV_VERSION,
@@ -336,6 +359,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA9888_HW_2_0_DEV_VERSION,
@@ -374,6 +400,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA9377_HW_1_0_DEV_VERSION,
@@ -402,6 +431,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA9377_HW_1_1_DEV_VERSION,
@@ -432,6 +464,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = QCA4019_HW_1_0_DEV_VERSION,
@@ -467,6 +502,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = 0x20,
                .target_64bit = false,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+               .per_ce_irq = false,
+               .shadow_reg_support = false,
+               .rri_on_ddr = false,
        },
        {
                .id = WCN3990_HW_1_0_DEV_VERSION,
@@ -487,6 +525,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                .num_wds_entries = TARGET_HL_10_TLV_NUM_WDS_ENTRIES,
                .target_64bit = true,
                .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL_DUAL_MAC,
+               .per_ce_irq = true,
+               .shadow_reg_support = true,
+               .rri_on_ddr = true,
        },
 };
 
@@ -1253,14 +1294,61 @@ out:
        return ret;
 }
 
+static int ath10k_core_search_bd(struct ath10k *ar,
+                                const char *boardname,
+                                const u8 *data,
+                                size_t len)
+{
+       size_t ie_len;
+       struct ath10k_fw_ie *hdr;
+       int ret = -ENOENT, ie_id;
+
+       while (len > sizeof(struct ath10k_fw_ie)) {
+               hdr = (struct ath10k_fw_ie *)data;
+               ie_id = le32_to_cpu(hdr->id);
+               ie_len = le32_to_cpu(hdr->len);
+
+               len -= sizeof(*hdr);
+               data = hdr->data;
+
+               if (len < ALIGN(ie_len, 4)) {
+                       ath10k_err(ar, "invalid length for board ie_id %d ie_len %zu len %zu\n",
+                                  ie_id, ie_len, len);
+                       return -EINVAL;
+               }
+
+               switch (ie_id) {
+               case ATH10K_BD_IE_BOARD:
+                       ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len,
+                                                           boardname);
+                       if (ret == -ENOENT)
+                               /* no match found, continue */
+                               break;
+
+                       /* either found or error, so stop searching */
+                       goto out;
+               }
+
+               /* jump over the padding */
+               ie_len = ALIGN(ie_len, 4);
+
+               len -= ie_len;
+               data += ie_len;
+       }
+
+out:
+       /* return result of parse_bd_ie_board() or -ENOENT */
+       return ret;
+}
+
 static int ath10k_core_fetch_board_data_api_n(struct ath10k *ar,
                                              const char *boardname,
+                                             const char *fallback_boardname,
                                              const char *filename)
 {
-       size_t len, magic_len, ie_len;
-       struct ath10k_fw_ie *hdr;
+       size_t len, magic_len;
        const u8 *data;
-       int ret, ie_id;
+       int ret;
 
        ar->normal_mode_fw.board = ath10k_fetch_fw_file(ar,
                                                        ar->hw_params.fw.dir,
@@ -1298,69 +1386,23 @@ static int ath10k_core_fetch_board_data_api_n(struct ath10k *ar,
        data += magic_len;
        len -= magic_len;
 
-       while (len > sizeof(struct ath10k_fw_ie)) {
-               hdr = (struct ath10k_fw_ie *)data;
-               ie_id = le32_to_cpu(hdr->id);
-               ie_len = le32_to_cpu(hdr->len);
-
-               len -= sizeof(*hdr);
-               data = hdr->data;
-
-               if (len < ALIGN(ie_len, 4)) {
-                       ath10k_err(ar, "invalid length for board ie_id %d ie_len %zu len %zu\n",
-                                  ie_id, ie_len, len);
-                       ret = -EINVAL;
-                       goto err;
-               }
-
-               switch (ie_id) {
-               case ATH10K_BD_IE_BOARD:
-                       ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len,
-                                                           boardname);
-                       if (ret == -ENOENT && ar->id.bdf_ext[0] != '\0') {
-                               /* try default bdf if variant was not found */
-                               char *s, *v = ",variant=";
-                               char boardname2[100];
-
-                               strlcpy(boardname2, boardname,
-                                       sizeof(boardname2));
-
-                               s = strstr(boardname2, v);
-                               if (s)
-                                       *s = '\0';  /* strip ",variant=%s" */
-
-                               ret = ath10k_core_parse_bd_ie_board(ar, data,
-                                                                   ie_len,
-                                                                   boardname2);
-                       }
-
-                       if (ret == -ENOENT)
-                               /* no match found, continue */
-                               break;
-                       else if (ret)
-                               /* there was an error, bail out */
-                               goto err;
+       /* attempt to find boardname in the IE list */
+       ret = ath10k_core_search_bd(ar, boardname, data, len);
 
-                       /* board data found */
-                       goto out;
-               }
+       /* if we didn't find it and have a fallback name, try that */
+       if (ret == -ENOENT && fallback_boardname)
+               ret = ath10k_core_search_bd(ar, fallback_boardname, data, len);
 
-               /* jump over the padding */
-               ie_len = ALIGN(ie_len, 4);
-
-               len -= ie_len;
-               data += ie_len;
-       }
-
-out:
-       if (!ar->normal_mode_fw.board_data || !ar->normal_mode_fw.board_len) {
+       if (ret == -ENOENT) {
                ath10k_err(ar,
                           "failed to fetch board data for %s from %s/%s\n",
                           boardname, ar->hw_params.fw.dir, filename);
                ret = -ENODATA;
-               goto err;
        }
 
+       if (ret)
+               goto err;
+
        return 0;
 
 err:
@@ -1369,12 +1411,12 @@ err:
 }
 
 static int ath10k_core_create_board_name(struct ath10k *ar, char *name,
-                                        size_t name_len)
+                                        size_t name_len, bool with_variant)
 {
        /* strlen(',variant=') + strlen(ar->id.bdf_ext) */
        char variant[9 + ATH10K_SMBIOS_BDF_EXT_STR_LENGTH] = { 0 };
 
-       if (ar->id.bdf_ext[0] != '\0')
+       if (with_variant && ar->id.bdf_ext[0] != '\0')
                scnprintf(variant, sizeof(variant), ",variant=%s",
                          ar->id.bdf_ext);
 
@@ -1400,17 +1442,26 @@ out:
 
 static int ath10k_core_fetch_board_file(struct ath10k *ar)
 {
-       char boardname[100];
+       char boardname[100], fallback_boardname[100];
        int ret;
 
-       ret = ath10k_core_create_board_name(ar, boardname, sizeof(boardname));
+       ret = ath10k_core_create_board_name(ar, boardname,
+                                           sizeof(boardname), true);
        if (ret) {
                ath10k_err(ar, "failed to create board name: %d", ret);
                return ret;
        }
 
+       ret = ath10k_core_create_board_name(ar, fallback_boardname,
+                                           sizeof(boardname), false);
+       if (ret) {
+               ath10k_err(ar, "failed to create fallback board name: %d", ret);
+               return ret;
+       }
+
        ar->bd_api = 2;
        ret = ath10k_core_fetch_board_data_api_n(ar, boardname,
+                                                fallback_boardname,
                                                 ATH10K_BOARD_API2_FILE);
        if (!ret)
                goto success;
@@ -2472,6 +2523,14 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
                ar->hw->wiphy->hw_version = target_info.version;
                break;
        case ATH10K_BUS_SNOC:
+               memset(&target_info, 0, sizeof(target_info));
+               ret = ath10k_hif_get_target_info(ar, &target_info);
+               if (ret) {
+                       ath10k_err(ar, "could not get target info (%d)\n", ret);
+                       goto err_power_down;
+               }
+               ar->target_version = target_info.version;
+               ar->hw->wiphy->hw_version = target_info.version;
                break;
        default:
                ath10k_err(ar, "incorrect hif bus type: %d\n", ar->hif.bus);
index c17d805..e4ac8f2 100644 (file)
@@ -52,6 +52,8 @@
 /* Antenna noise floor */
 #define ATH10K_DEFAULT_NOISE_FLOOR -95
 
+#define ATH10K_INVALID_RSSI 128
+
 #define ATH10K_MAX_NUM_MGMT_PENDING 128
 
 /* number of failed packets (20 packets with 16 sw reties each) */
index 6da4e33..1a59ea0 100644 (file)
 
 #include <linux/kernel.h>
 #include "core.h"
+#include "bmi.h"
 #include "debug.h"
 
 struct ath10k_hif_sg_item {
        u16 transfer_id;
        void *transfer_context; /* NULL = tx completion callback not called */
        void *vaddr; /* for debugging mostly */
-       u32 paddr;
+       dma_addr_t paddr;
        u16 len;
 };
 
@@ -93,6 +94,9 @@ struct ath10k_hif_ops {
        /* fetch calibration data from target eeprom */
        int (*fetch_cal_eeprom)(struct ath10k *ar, void **data,
                                size_t *data_len);
+
+       int (*get_target_info)(struct ath10k *ar,
+                              struct bmi_target_info *target_info);
 };
 
 static inline int ath10k_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
@@ -218,4 +222,13 @@ static inline int ath10k_hif_fetch_cal_eeprom(struct ath10k *ar,
        return ar->hif.ops->fetch_cal_eeprom(ar, data, data_len);
 }
 
+static inline int ath10k_hif_get_target_info(struct ath10k *ar,
+                                            struct bmi_target_info *tgt_info)
+{
+       if (!ar->hif.ops->get_target_info)
+               return -EOPNOTSUPP;
+
+       return ar->hif.ops->get_target_info(ar, tgt_info);
+}
+
 #endif /* _HIF_H_ */
index 492dc5b..8902720 100644 (file)
@@ -542,8 +542,14 @@ static const char *htc_service_name(enum ath10k_htc_svc_id id)
                return "NMI Data";
        case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
                return "HTT Data";
+       case ATH10K_HTC_SVC_ID_HTT_DATA2_MSG:
+               return "HTT Data";
+       case ATH10K_HTC_SVC_ID_HTT_DATA3_MSG:
+               return "HTT Data";
        case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
                return "RAW";
+       case ATH10K_HTC_SVC_ID_HTT_LOG_MSG:
+               return "PKTLOG";
        }
 
        return "Unknown";
index a2f8814..3487759 100644 (file)
@@ -248,6 +248,7 @@ enum ath10k_htc_svc_gid {
        ATH10K_HTC_SVC_GRP_WMI = 1,
        ATH10K_HTC_SVC_GRP_NMI = 2,
        ATH10K_HTC_SVC_GRP_HTT = 3,
+       ATH10K_LOG_SERVICE_GROUP = 6,
 
        ATH10K_HTC_SVC_GRP_TEST = 254,
        ATH10K_HTC_SVC_GRP_LAST = 255,
@@ -273,6 +274,9 @@ enum ath10k_htc_svc_id {
 
        ATH10K_HTC_SVC_ID_HTT_DATA_MSG  = SVC(ATH10K_HTC_SVC_GRP_HTT, 0),
 
+       ATH10K_HTC_SVC_ID_HTT_DATA2_MSG = SVC(ATH10K_HTC_SVC_GRP_HTT, 1),
+       ATH10K_HTC_SVC_ID_HTT_DATA3_MSG = SVC(ATH10K_HTC_SVC_GRP_HTT, 2),
+       ATH10K_HTC_SVC_ID_HTT_LOG_MSG = SVC(ATH10K_LOG_SERVICE_GROUP, 0),
        /* raw stream service (i.e. flash, tcmd, calibration apps) */
        ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH10K_HTC_SVC_GRP_TEST, 0),
 };
index 625198d..21a67f8 100644 (file)
@@ -257,11 +257,11 @@ int ath10k_htt_setup(struct ath10k_htt *htt)
                return status;
        }
 
-       status = htt->tx_ops->htt_send_frag_desc_bank_cfg(htt);
+       status = ath10k_htt_send_frag_desc_bank_cfg(htt);
        if (status)
                return status;
 
-       status = htt->tx_ops->htt_send_rx_ring_cfg(htt);
+       status = ath10k_htt_send_rx_ring_cfg(htt);
        if (status) {
                ath10k_warn(ar, "failed to setup rx ring: %d\n",
                            status);
index 8cc2a8b..5d3ff80 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -127,6 +128,19 @@ struct htt_msdu_ext_desc_64 {
                                 | HTT_MSDU_EXT_DESC_FLAG_TCP_IPV4_CSUM_ENABLE \
                                 | HTT_MSDU_EXT_DESC_FLAG_TCP_IPV6_CSUM_ENABLE)
 
+#define HTT_MSDU_EXT_DESC_FLAG_IPV4_CSUM_ENABLE_64             BIT(16)
+#define HTT_MSDU_EXT_DESC_FLAG_UDP_IPV4_CSUM_ENABLE_64         BIT(17)
+#define HTT_MSDU_EXT_DESC_FLAG_UDP_IPV6_CSUM_ENABLE_64         BIT(18)
+#define HTT_MSDU_EXT_DESC_FLAG_TCP_IPV4_CSUM_ENABLE_64         BIT(19)
+#define HTT_MSDU_EXT_DESC_FLAG_TCP_IPV6_CSUM_ENABLE_64         BIT(20)
+#define HTT_MSDU_EXT_DESC_FLAG_PARTIAL_CSUM_ENABLE_64          BIT(21)
+
+#define HTT_MSDU_CHECKSUM_ENABLE_64  (HTT_MSDU_EXT_DESC_FLAG_IPV4_CSUM_ENABLE_64 \
+                                    | HTT_MSDU_EXT_DESC_FLAG_UDP_IPV4_CSUM_ENABLE_64 \
+                                    | HTT_MSDU_EXT_DESC_FLAG_UDP_IPV6_CSUM_ENABLE_64 \
+                                    | HTT_MSDU_EXT_DESC_FLAG_TCP_IPV4_CSUM_ENABLE_64 \
+                                    | HTT_MSDU_EXT_DESC_FLAG_TCP_IPV6_CSUM_ENABLE_64)
+
 enum htt_data_tx_desc_flags0 {
        HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT = 1 << 0,
        HTT_DATA_TX_DESC_FLAGS0_NO_AGGR         = 1 << 1,
@@ -533,12 +547,18 @@ struct htt_ver_resp {
        u8 rsvd0;
 } __packed;
 
+#define HTT_MGMT_TX_CMPL_FLAG_ACK_RSSI BIT(0)
+
+#define HTT_MGMT_TX_CMPL_INFO_ACK_RSSI_MASK    GENMASK(7, 0)
+
 struct htt_mgmt_tx_completion {
        u8 rsvd0;
        u8 rsvd1;
-       u8 rsvd2;
+       u8 flags;
        __le32 desc_id;
        __le32 status;
+       __le32 ppdu_id;
+       __le32 info;
 } __packed;
 
 #define HTT_RX_INDICATION_INFO0_EXT_TID_MASK  (0x1F)
@@ -1648,6 +1668,7 @@ struct htt_resp {
 struct htt_tx_done {
        u16 msdu_id;
        u16 status;
+       u8 ack_rssi;
 };
 
 enum htt_tx_compl_state {
@@ -1848,6 +1869,57 @@ struct ath10k_htt_tx_ops {
        void (*htt_free_txbuff)(struct ath10k_htt *htt);
 };
 
+static inline int ath10k_htt_send_rx_ring_cfg(struct ath10k_htt *htt)
+{
+       if (!htt->tx_ops->htt_send_rx_ring_cfg)
+               return -EOPNOTSUPP;
+
+       return htt->tx_ops->htt_send_rx_ring_cfg(htt);
+}
+
+static inline int ath10k_htt_send_frag_desc_bank_cfg(struct ath10k_htt *htt)
+{
+       if (!htt->tx_ops->htt_send_frag_desc_bank_cfg)
+               return -EOPNOTSUPP;
+
+       return htt->tx_ops->htt_send_frag_desc_bank_cfg(htt);
+}
+
+static inline int ath10k_htt_alloc_frag_desc(struct ath10k_htt *htt)
+{
+       if (!htt->tx_ops->htt_alloc_frag_desc)
+               return -EOPNOTSUPP;
+
+       return htt->tx_ops->htt_alloc_frag_desc(htt);
+}
+
+static inline void ath10k_htt_free_frag_desc(struct ath10k_htt *htt)
+{
+       if (htt->tx_ops->htt_free_frag_desc)
+               htt->tx_ops->htt_free_frag_desc(htt);
+}
+
+static inline int ath10k_htt_tx(struct ath10k_htt *htt,
+                               enum ath10k_hw_txrx_mode txmode,
+                               struct sk_buff *msdu)
+{
+       return htt->tx_ops->htt_tx(htt, txmode, msdu);
+}
+
+static inline int ath10k_htt_alloc_txbuff(struct ath10k_htt *htt)
+{
+       if (!htt->tx_ops->htt_alloc_txbuff)
+               return -EOPNOTSUPP;
+
+       return htt->tx_ops->htt_alloc_txbuff(htt);
+}
+
+static inline void ath10k_htt_free_txbuff(struct ath10k_htt *htt)
+{
+       if (htt->tx_ops->htt_free_txbuff)
+               htt->tx_ops->htt_free_txbuff(htt);
+}
+
 struct ath10k_htt_rx_ops {
        size_t (*htt_get_rx_ring_size)(struct ath10k_htt *htt);
        void (*htt_config_paddrs_ring)(struct ath10k_htt *htt, void *vaddr);
@@ -1857,6 +1929,43 @@ struct ath10k_htt_rx_ops {
        void (*htt_reset_paddrs_ring)(struct ath10k_htt *htt, int idx);
 };
 
+static inline size_t ath10k_htt_get_rx_ring_size(struct ath10k_htt *htt)
+{
+       if (!htt->rx_ops->htt_get_rx_ring_size)
+               return 0;
+
+       return htt->rx_ops->htt_get_rx_ring_size(htt);
+}
+
+static inline void ath10k_htt_config_paddrs_ring(struct ath10k_htt *htt,
+                                                void *vaddr)
+{
+       if (htt->rx_ops->htt_config_paddrs_ring)
+               htt->rx_ops->htt_config_paddrs_ring(htt, vaddr);
+}
+
+static inline void ath10k_htt_set_paddrs_ring(struct ath10k_htt *htt,
+                                             dma_addr_t paddr,
+                                             int idx)
+{
+       if (htt->rx_ops->htt_set_paddrs_ring)
+               htt->rx_ops->htt_set_paddrs_ring(htt, paddr, idx);
+}
+
+static inline void *ath10k_htt_get_vaddr_ring(struct ath10k_htt *htt)
+{
+       if (!htt->rx_ops->htt_get_vaddr_ring)
+               return NULL;
+
+       return htt->rx_ops->htt_get_vaddr_ring(htt);
+}
+
+static inline void ath10k_htt_reset_paddrs_ring(struct ath10k_htt *htt, int idx)
+{
+       if (htt->rx_ops->htt_reset_paddrs_ring)
+               htt->rx_ops->htt_reset_paddrs_ring(htt, idx);
+}
+
 #define RX_HTT_HDR_STATUS_LEN 64
 
 /* This structure layout is programmed via rx ring setup
index 5e02e26..bd23f69 100644 (file)
@@ -25,6 +25,7 @@
 #include "mac.h"
 
 #include <linux/log2.h>
+#include <linux/bitfield.h>
 
 /* when under memory pressure rx ring refill may fail and needs a retry */
 #define HTT_RX_RING_REFILL_RETRY_MS 50
@@ -181,7 +182,7 @@ static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
                rxcb = ATH10K_SKB_RXCB(skb);
                rxcb->paddr = paddr;
                htt->rx_ring.netbufs_ring[idx] = skb;
-               htt->rx_ops->htt_set_paddrs_ring(htt, paddr, idx);
+               ath10k_htt_set_paddrs_ring(htt, paddr, idx);
                htt->rx_ring.fill_cnt++;
 
                if (htt->rx_ring.in_ord_rx) {
@@ -286,8 +287,8 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt)
        ath10k_htt_rx_ring_free(htt);
 
        dma_free_coherent(htt->ar->dev,
-                         htt->rx_ops->htt_get_rx_ring_size(htt),
-                         htt->rx_ops->htt_get_vaddr_ring(htt),
+                         ath10k_htt_get_rx_ring_size(htt),
+                         ath10k_htt_get_vaddr_ring(htt),
                          htt->rx_ring.base_paddr);
 
        dma_free_coherent(htt->ar->dev,
@@ -314,7 +315,7 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
        idx = htt->rx_ring.sw_rd_idx.msdu_payld;
        msdu = htt->rx_ring.netbufs_ring[idx];
        htt->rx_ring.netbufs_ring[idx] = NULL;
-       htt->rx_ops->htt_reset_paddrs_ring(htt, idx);
+       ath10k_htt_reset_paddrs_ring(htt, idx);
 
        idx++;
        idx &= htt->rx_ring.size_mask;
@@ -586,13 +587,13 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
        if (!htt->rx_ring.netbufs_ring)
                goto err_netbuf;
 
-       size = htt->rx_ops->htt_get_rx_ring_size(htt);
+       size = ath10k_htt_get_rx_ring_size(htt);
 
        vaddr_ring = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_KERNEL);
        if (!vaddr_ring)
                goto err_dma_ring;
 
-       htt->rx_ops->htt_config_paddrs_ring(htt, vaddr_ring);
+       ath10k_htt_config_paddrs_ring(htt, vaddr_ring);
        htt->rx_ring.base_paddr = paddr;
 
        vaddr = dma_alloc_coherent(htt->ar->dev,
@@ -626,7 +627,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
 
 err_dma_idx:
        dma_free_coherent(htt->ar->dev,
-                         htt->rx_ops->htt_get_rx_ring_size(htt),
+                         ath10k_htt_get_rx_ring_size(htt),
                          vaddr_ring,
                          htt->rx_ring.base_paddr);
 err_dma_ring:
@@ -2719,12 +2720,21 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
        case HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION: {
                struct htt_tx_done tx_done = {};
                int status = __le32_to_cpu(resp->mgmt_tx_completion.status);
+               int info = __le32_to_cpu(resp->mgmt_tx_completion.info);
 
                tx_done.msdu_id = __le32_to_cpu(resp->mgmt_tx_completion.desc_id);
 
                switch (status) {
                case HTT_MGMT_TX_STATUS_OK:
                        tx_done.status = HTT_TX_COMPL_STATE_ACK;
+                       if (test_bit(WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS,
+                                    ar->wmi.svc_map) &&
+                           (resp->mgmt_tx_completion.flags &
+                            HTT_MGMT_TX_CMPL_FLAG_ACK_RSSI)) {
+                               tx_done.ack_rssi =
+                               FIELD_GET(HTT_MGMT_TX_CMPL_INFO_ACK_RSSI_MASK,
+                                         info);
+                       }
                        break;
                case HTT_MGMT_TX_STATUS_RETRY:
                        tx_done.status = HTT_TX_COMPL_STATE_NOACK;
index d334b7b..5d8b97a 100644 (file)
@@ -443,13 +443,13 @@ static int ath10k_htt_tx_alloc_buf(struct ath10k_htt *htt)
        struct ath10k *ar = htt->ar;
        int ret;
 
-       ret = htt->tx_ops->htt_alloc_txbuff(htt);
+       ret = ath10k_htt_alloc_txbuff(htt);
        if (ret) {
                ath10k_err(ar, "failed to alloc cont tx buffer: %d\n", ret);
                return ret;
        }
 
-       ret = htt->tx_ops->htt_alloc_frag_desc(htt);
+       ret = ath10k_htt_alloc_frag_desc(htt);
        if (ret) {
                ath10k_err(ar, "failed to alloc cont frag desc: %d\n", ret);
                goto free_txbuf;
@@ -473,10 +473,10 @@ free_txq:
        ath10k_htt_tx_free_txq(htt);
 
 free_frag_desc:
-       htt->tx_ops->htt_free_frag_desc(htt);
+       ath10k_htt_free_frag_desc(htt);
 
 free_txbuf:
-       htt->tx_ops->htt_free_txbuff(htt);
+       ath10k_htt_free_txbuff(htt);
 
        return ret;
 }
@@ -530,9 +530,9 @@ void ath10k_htt_tx_destroy(struct ath10k_htt *htt)
        if (!htt->tx_mem_allocated)
                return;
 
-       htt->tx_ops->htt_free_txbuff(htt);
+       ath10k_htt_free_txbuff(htt);
        ath10k_htt_tx_free_txq(htt);
-       htt->tx_ops->htt_free_frag_desc(htt);
+       ath10k_htt_free_frag_desc(htt);
        ath10k_htt_tx_free_txdone_fifo(htt);
        htt->tx_mem_allocated = false;
 }
@@ -1475,8 +1475,11 @@ static int ath10k_htt_tx_64(struct ath10k_htt *htt,
            !test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
                flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
                flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
-               if (ar->hw_params.continuous_frag_desc)
-                       ext_desc->flags |= HTT_MSDU_CHECKSUM_ENABLE;
+               if (ar->hw_params.continuous_frag_desc) {
+                       memset(ext_desc->tso_flag, 0, sizeof(ext_desc->tso_flag));
+                       ext_desc->tso_flag[3] |=
+                               __cpu_to_le32(HTT_MSDU_CHECKSUM_ENABLE_64);
+               }
        }
 
        /* Prevent firmware from sending up tx inspection requests. There's
index 497ac33..677535b 100644 (file)
@@ -310,6 +310,12 @@ static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = {
        .wm_high        = &wcn3990_dst_wm_high,
 };
 
+static struct ath10k_hw_ce_ctrl1_upd wcn3990_ctrl1_upd = {
+       .shift = 19,
+       .mask = 0x00080000,
+       .enable = 0x00000000,
+};
+
 const struct ath10k_hw_ce_regs wcn3990_ce_regs = {
        .sr_base_addr           = 0x00000000,
        .sr_size_addr           = 0x00000008,
@@ -320,8 +326,6 @@ const struct ath10k_hw_ce_regs wcn3990_ce_regs = {
        .dst_wr_index_addr      = 0x00000040,
        .current_srri_addr      = 0x00000044,
        .current_drri_addr      = 0x00000048,
-       .ddr_addr_for_rri_low   = 0x00000004,
-       .ddr_addr_for_rri_high  = 0x00000008,
        .ce_rri_low             = 0x0024C004,
        .ce_rri_high            = 0x0024C008,
        .host_ie_addr           = 0x0000002c,
@@ -331,6 +335,7 @@ const struct ath10k_hw_ce_regs wcn3990_ce_regs = {
        .misc_regs              = &wcn3990_misc_reg,
        .wm_srcr                = &wcn3990_wm_src_ring,
        .wm_dstr                = &wcn3990_wm_dst_ring,
+       .upd                    = &wcn3990_ctrl1_upd,
 };
 
 const struct ath10k_hw_values wcn3990_values = {
index 413b1b4..b8bdabe 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -131,7 +132,7 @@ enum qca9377_chip_id_rev {
 
 /* WCN3990 1.0 definitions */
 #define WCN3990_HW_1_0_DEV_VERSION     ATH10K_HW_WCN3990
-#define WCN3990_HW_1_0_FW_DIR          ATH10K_FW_DIR "/WCN3990/hw3.0"
+#define WCN3990_HW_1_0_FW_DIR          ATH10K_FW_DIR "/WCN3990/hw1.0"
 
 #define ATH10K_FW_FILE_BASE            "firmware"
 #define ATH10K_FW_API_MAX              6
@@ -335,6 +336,12 @@ struct ath10k_hw_ce_dst_src_wm_regs {
        struct ath10k_hw_ce_regs_addr_map *wm_low;
        struct ath10k_hw_ce_regs_addr_map *wm_high; };
 
+struct ath10k_hw_ce_ctrl1_upd {
+       u32 shift;
+       u32 mask;
+       u32 enable;
+};
+
 struct ath10k_hw_ce_regs {
        u32 sr_base_addr;
        u32 sr_size_addr;
@@ -357,7 +364,9 @@ struct ath10k_hw_ce_regs {
        struct ath10k_hw_ce_cmd_halt *cmd_halt;
        struct ath10k_hw_ce_host_ie *host_ie;
        struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr;
-       struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr; };
+       struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr;
+       struct ath10k_hw_ce_ctrl1_upd *upd;
+};
 
 struct ath10k_hw_values {
        u32 rtc_state_val_on;
@@ -568,6 +577,15 @@ struct ath10k_hw_params {
 
        /* Target rx ring fill level */
        u32 rx_ring_fill_level;
+
+       /* target supporting per ce IRQ */
+       bool per_ce_irq;
+
+       /* target supporting shadow register for ce write */
+       bool shadow_reg_support;
+
+       /* target supporting retention restore on ddr */
+       bool rri_on_ddr;
 };
 
 struct htt_rx_desc;
index bf05a36..3d7119a 100644 (file)
@@ -3598,7 +3598,7 @@ static int ath10k_mac_tx_submit(struct ath10k *ar,
 
        switch (txpath) {
        case ATH10K_MAC_TX_HTT:
-               ret = htt->tx_ops->htt_tx(htt, txmode, skb);
+               ret = ath10k_htt_tx(htt, txmode, skb);
                break;
        case ATH10K_MAC_TX_HTT_MGMT:
                ret = ath10k_htt_mgmt_tx(htt, skb);
@@ -4679,6 +4679,13 @@ static int ath10k_start(struct ieee80211_hw *hw)
                }
        }
 
+       param = ar->wmi.pdev_param->idle_ps_config;
+       ret = ath10k_wmi_pdev_set_param(ar, param, 1);
+       if (ret && ret != -EOPNOTSUPP) {
+               ath10k_warn(ar, "failed to enable idle_ps_config: %d\n", ret);
+               goto err_core_stop;
+       }
+
        __ath10k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask);
 
        /*
@@ -5717,6 +5724,12 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw,
                arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
        }
 
+       if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+               arg.scan_ctrl_flags |=  WMI_SCAN_ADD_SPOOFED_MAC_IN_PROBE_REQ;
+               ether_addr_copy(arg.mac_addr.addr, req->mac_addr);
+               ether_addr_copy(arg.mac_mask.addr, req->mac_addr_mask);
+       }
+
        if (req->n_channels) {
                arg.n_channels = req->n_channels;
                for (i = 0; i < arg.n_channels; i++)
@@ -8433,6 +8446,17 @@ int ath10k_mac_register(struct ath10k *ar)
                goto err_dfs_detector_exit;
        }
 
+       if (test_bit(WMI_SERVICE_SPOOF_MAC_SUPPORT, ar->wmi.svc_map)) {
+               ret = ath10k_wmi_scan_prob_req_oui(ar, ar->mac_addr);
+               if (ret) {
+                       ath10k_err(ar, "failed to set prob req oui: %i\n", ret);
+                       goto err_dfs_detector_exit;
+               }
+
+               ar->hw->wiphy->features |=
+                       NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+       }
+
        ar->hw->wiphy->cipher_suites = cipher_suites;
 
        /* QCA988x and QCA6174 family chips do not support CCMP-256, GCMP-128
index fd1566c..af2cf55 100644 (file)
@@ -1383,8 +1383,8 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
 
        for (i = 0; i < n_items - 1; i++) {
                ath10k_dbg(ar, ATH10K_DBG_PCI,
-                          "pci tx item %d paddr 0x%08x len %d n_items %d\n",
-                          i, items[i].paddr, items[i].len, n_items);
+                          "pci tx item %d paddr %pad len %d n_items %d\n",
+                          i, &items[i].paddr, items[i].len, n_items);
                ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci tx data: ",
                                items[i].vaddr, items[i].len);
 
@@ -1401,8 +1401,8 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
        /* `i` is equal to `n_items -1` after for() */
 
        ath10k_dbg(ar, ATH10K_DBG_PCI,
-                  "pci tx item %d paddr 0x%08x len %d n_items %d\n",
-                  i, items[i].paddr, items[i].len, n_items);
+                  "pci tx item %d paddr %pad len %d n_items %d\n",
+                  i, &items[i].paddr, items[i].len, n_items);
        ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci tx data: ",
                        items[i].vaddr, items[i].len);
 
index 03a69e5..2d04c54 100644 (file)
@@ -1957,25 +1957,25 @@ static int ath10k_sdio_probe(struct sdio_func *func,
        ar_sdio = ath10k_sdio_priv(ar);
 
        ar_sdio->irq_data.irq_proc_reg =
-               kzalloc(sizeof(struct ath10k_sdio_irq_proc_regs),
-                       GFP_KERNEL);
+               devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_proc_regs),
+                            GFP_KERNEL);
        if (!ar_sdio->irq_data.irq_proc_reg) {
                ret = -ENOMEM;
                goto err_core_destroy;
        }
 
        ar_sdio->irq_data.irq_en_reg =
-               kzalloc(sizeof(struct ath10k_sdio_irq_enable_regs),
-                       GFP_KERNEL);
+               devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_enable_regs),
+                            GFP_KERNEL);
        if (!ar_sdio->irq_data.irq_en_reg) {
                ret = -ENOMEM;
-               goto err_free_proc_reg;
+               goto err_core_destroy;
        }
 
-       ar_sdio->bmi_buf = kzalloc(BMI_MAX_CMDBUF_SIZE, GFP_KERNEL);
+       ar_sdio->bmi_buf = devm_kzalloc(ar->dev, BMI_MAX_CMDBUF_SIZE, GFP_KERNEL);
        if (!ar_sdio->bmi_buf) {
                ret = -ENOMEM;
-               goto err_free_en_reg;
+               goto err_core_destroy;
        }
 
        ar_sdio->func = func;
@@ -1995,7 +1995,7 @@ static int ath10k_sdio_probe(struct sdio_func *func,
        ar_sdio->workqueue = create_singlethread_workqueue("ath10k_sdio_wq");
        if (!ar_sdio->workqueue) {
                ret = -ENOMEM;
-               goto err_free_bmi_buf;
+               goto err_core_destroy;
        }
 
        for (i = 0; i < ATH10K_SDIO_BUS_REQUEST_MAX_NUM; i++)
@@ -2011,7 +2011,7 @@ static int ath10k_sdio_probe(struct sdio_func *func,
                ret = -ENODEV;
                ath10k_err(ar, "unsupported device id %u (0x%x)\n",
                           dev_id_base, id->device);
-               goto err_free_bmi_buf;
+               goto err_core_destroy;
        }
 
        ar->id.vendor = id->vendor;
@@ -2040,12 +2040,6 @@ static int ath10k_sdio_probe(struct sdio_func *func,
 
 err_free_wq:
        destroy_workqueue(ar_sdio->workqueue);
-err_free_bmi_buf:
-       kfree(ar_sdio->bmi_buf);
-err_free_en_reg:
-       kfree(ar_sdio->irq_data.irq_en_reg);
-err_free_proc_reg:
-       kfree(ar_sdio->irq_data.irq_proc_reg);
 err_core_destroy:
        ath10k_core_destroy(ar);
 
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
new file mode 100644 (file)
index 0000000..47a4d2a
--- /dev/null
@@ -0,0 +1,1414 @@
+/*
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include "debug.h"
+#include "hif.h"
+#include "htc.h"
+#include "ce.h"
+#include "snoc.h"
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#define  WCN3990_CE_ATTR_FLAGS 0
+#define ATH10K_SNOC_RX_POST_RETRY_MS 50
+#define CE_POLL_PIPE 4
+
+static char *const ce_name[] = {
+       "WLAN_CE_0",
+       "WLAN_CE_1",
+       "WLAN_CE_2",
+       "WLAN_CE_3",
+       "WLAN_CE_4",
+       "WLAN_CE_5",
+       "WLAN_CE_6",
+       "WLAN_CE_7",
+       "WLAN_CE_8",
+       "WLAN_CE_9",
+       "WLAN_CE_10",
+       "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_wcn3990_clk_info clk_cfg[] = {
+       {NULL, "cxo_ref_clk_pin", 0, false},
+};
+
+static void ath10k_snoc_htc_tx_cb(struct ath10k_ce_pipe *ce_state);
+static void ath10k_snoc_htt_tx_cb(struct ath10k_ce_pipe *ce_state);
+static void ath10k_snoc_htc_rx_cb(struct ath10k_ce_pipe *ce_state);
+static void ath10k_snoc_htt_rx_cb(struct ath10k_ce_pipe *ce_state);
+static void ath10k_snoc_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state);
+
+static const struct ath10k_snoc_drv_priv drv_priv = {
+       .hw_rev = ATH10K_HW_WCN3990,
+       .dma_mask = DMA_BIT_MASK(37),
+};
+
+static struct ce_attr host_ce_config_wlan[] = {
+       /* CE0: host->target HTC control streams */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 16,
+               .src_sz_max = 2048,
+               .dest_nentries = 0,
+               .send_cb = ath10k_snoc_htc_tx_cb,
+       },
+
+       /* CE1: target->host HTT + HTC control */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 2048,
+               .dest_nentries = 512,
+               .recv_cb = ath10k_snoc_htt_htc_rx_cb,
+       },
+
+       /* CE2: target->host WMI */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 2048,
+               .dest_nentries = 64,
+               .recv_cb = ath10k_snoc_htc_rx_cb,
+       },
+
+       /* CE3: host->target WMI */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 32,
+               .src_sz_max = 2048,
+               .dest_nentries = 0,
+               .send_cb = ath10k_snoc_htc_tx_cb,
+       },
+
+       /* CE4: host->target HTT */
+       {
+               .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
+               .src_nentries = 256,
+               .src_sz_max = 256,
+               .dest_nentries = 0,
+               .send_cb = ath10k_snoc_htt_tx_cb,
+       },
+
+       /* CE5: target->host HTT (ipa_uc->target ) */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 512,
+               .dest_nentries = 512,
+               .recv_cb = ath10k_snoc_htt_rx_cb,
+       },
+
+       /* CE6: target autonomous hif_memcpy */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 0,
+               .dest_nentries = 0,
+       },
+
+       /* CE7: ce_diag, the Diagnostic Window */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 2,
+               .src_sz_max = 2048,
+               .dest_nentries = 2,
+       },
+
+       /* CE8: Target to uMC */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 2048,
+               .dest_nentries = 128,
+       },
+
+       /* CE9 target->host HTT */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 2048,
+               .dest_nentries = 512,
+               .recv_cb = ath10k_snoc_htt_htc_rx_cb,
+       },
+
+       /* CE10: target->host HTT */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 2048,
+               .dest_nentries = 512,
+               .recv_cb = ath10k_snoc_htt_htc_rx_cb,
+       },
+
+       /* CE11: target -> host PKTLOG */
+       {
+               .flags = CE_ATTR_FLAGS,
+               .src_nentries = 0,
+               .src_sz_max = 2048,
+               .dest_nentries = 512,
+               .recv_cb = ath10k_snoc_htt_htc_rx_cb,
+       },
+};
+
+static struct service_to_pipe target_service_to_ce_map_wlan[] = {
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VO),
+               __cpu_to_le32(PIPEDIR_OUT),     /* out = UL = host -> target */
+               __cpu_to_le32(3),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VO),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(2),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BK),
+               __cpu_to_le32(PIPEDIR_OUT),     /* out = UL = host -> target */
+               __cpu_to_le32(3),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BK),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(2),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BE),
+               __cpu_to_le32(PIPEDIR_OUT),     /* out = UL = host -> target */
+               __cpu_to_le32(3),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BE),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(2),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VI),
+               __cpu_to_le32(PIPEDIR_OUT),     /* out = UL = host -> target */
+               __cpu_to_le32(3),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VI),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(2),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_CONTROL),
+               __cpu_to_le32(PIPEDIR_OUT),     /* out = UL = host -> target */
+               __cpu_to_le32(3),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_CONTROL),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(2),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_RSVD_CTRL),
+               __cpu_to_le32(PIPEDIR_OUT),     /* out = UL = host -> target */
+               __cpu_to_le32(0),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_RSVD_CTRL),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(2),
+       },
+       { /* not used */
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS),
+               __cpu_to_le32(PIPEDIR_OUT),     /* out = UL = host -> target */
+               __cpu_to_le32(0),
+       },
+       { /* not used */
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(2),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA_MSG),
+               __cpu_to_le32(PIPEDIR_OUT),     /* out = UL = host -> target */
+               __cpu_to_le32(4),
+       },
+       {
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA_MSG),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(1),
+       },
+       { /* not used */
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS),
+               __cpu_to_le32(PIPEDIR_OUT),
+               __cpu_to_le32(5),
+       },
+       { /* in = DL = target -> host */
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA2_MSG),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(9),
+       },
+       { /* in = DL = target -> host */
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA3_MSG),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(10),
+       },
+       { /* in = DL = target -> host pktlog */
+               __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_LOG_MSG),
+               __cpu_to_le32(PIPEDIR_IN),      /* in = DL = target -> host */
+               __cpu_to_le32(11),
+       },
+       /* (Additions here) */
+
+       { /* must be last */
+               __cpu_to_le32(0),
+               __cpu_to_le32(0),
+               __cpu_to_le32(0),
+       },
+};
+
+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)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       u32 val;
+
+       val = ioread32(ar_snoc->mem + offset);
+
+       return val;
+}
+
+static int __ath10k_snoc_rx_post_buf(struct ath10k_snoc_pipe *pipe)
+{
+       struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl;
+       struct ath10k *ar = pipe->hif_ce_state;
+       struct ath10k_ce *ce = ath10k_ce_priv(ar);
+       struct sk_buff *skb;
+       dma_addr_t paddr;
+       int ret;
+
+       skb = dev_alloc_skb(pipe->buf_sz);
+       if (!skb)
+               return -ENOMEM;
+
+       WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
+
+       paddr = dma_map_single(ar->dev, skb->data,
+                              skb->len + skb_tailroom(skb),
+                              DMA_FROM_DEVICE);
+       if (unlikely(dma_mapping_error(ar->dev, paddr))) {
+               ath10k_warn(ar, "failed to dma map snoc rx buf\n");
+               dev_kfree_skb_any(skb);
+               return -EIO;
+       }
+
+       ATH10K_SKB_RXCB(skb)->paddr = paddr;
+
+       spin_lock_bh(&ce->ce_lock);
+       ret = ce_pipe->ops->ce_rx_post_buf(ce_pipe, skb, paddr);
+       spin_unlock_bh(&ce->ce_lock);
+       if (ret) {
+               dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void ath10k_snoc_rx_post_pipe(struct ath10k_snoc_pipe *pipe)
+{
+       struct ath10k *ar = pipe->hif_ce_state;
+       struct ath10k_ce *ce = ath10k_ce_priv(ar);
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl;
+       int ret, num;
+
+       if (pipe->buf_sz == 0)
+               return;
+
+       if (!ce_pipe->dest_ring)
+               return;
+
+       spin_lock_bh(&ce->ce_lock);
+       num = __ath10k_ce_rx_num_free_bufs(ce_pipe);
+       spin_unlock_bh(&ce->ce_lock);
+       while (num--) {
+               ret = __ath10k_snoc_rx_post_buf(pipe);
+               if (ret) {
+                       if (ret == -ENOSPC)
+                               break;
+                       ath10k_warn(ar, "failed to post rx buf: %d\n", ret);
+                       mod_timer(&ar_snoc->rx_post_retry, jiffies +
+                                 ATH10K_SNOC_RX_POST_RETRY_MS);
+                       break;
+               }
+       }
+}
+
+static void ath10k_snoc_rx_post(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       int i;
+
+       for (i = 0; i < CE_COUNT; i++)
+               ath10k_snoc_rx_post_pipe(&ar_snoc->pipe_info[i]);
+}
+
+static void ath10k_snoc_process_rx_cb(struct ath10k_ce_pipe *ce_state,
+                                     void (*callback)(struct ath10k *ar,
+                                                      struct sk_buff *skb))
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct ath10k_snoc_pipe *pipe_info =  &ar_snoc->pipe_info[ce_state->id];
+       struct sk_buff *skb;
+       struct sk_buff_head list;
+       void *transfer_context;
+       unsigned int nbytes, max_nbytes;
+
+       __skb_queue_head_init(&list);
+       while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
+                                            &nbytes) == 0) {
+               skb = transfer_context;
+               max_nbytes = skb->len + skb_tailroom(skb);
+               dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
+                                max_nbytes, DMA_FROM_DEVICE);
+
+               if (unlikely(max_nbytes < nbytes)) {
+                       ath10k_warn(ar, "rxed more than expected (nbytes %d, max %d)",
+                                   nbytes, max_nbytes);
+                       dev_kfree_skb_any(skb);
+                       continue;
+               }
+
+               skb_put(skb, nbytes);
+               __skb_queue_tail(&list, skb);
+       }
+
+       while ((skb = __skb_dequeue(&list))) {
+               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc rx ce pipe %d len %d\n",
+                          ce_state->id, skb->len);
+
+               callback(ar, skb);
+       }
+
+       ath10k_snoc_rx_post_pipe(pipe_info);
+}
+
+static void ath10k_snoc_htc_rx_cb(struct ath10k_ce_pipe *ce_state)
+{
+       ath10k_snoc_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler);
+}
+
+static void ath10k_snoc_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state)
+{
+       /* CE4 polling needs to be done whenever CE pipe which transports
+        * HTT Rx (target->host) is processed.
+        */
+       ath10k_ce_per_engine_service(ce_state->ar, CE_POLL_PIPE);
+
+       ath10k_snoc_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler);
+}
+
+static void ath10k_snoc_htt_rx_deliver(struct ath10k *ar, struct sk_buff *skb)
+{
+       skb_pull(skb, sizeof(struct ath10k_htc_hdr));
+       ath10k_htt_t2h_msg_handler(ar, skb);
+}
+
+static void ath10k_snoc_htt_rx_cb(struct ath10k_ce_pipe *ce_state)
+{
+       ath10k_ce_per_engine_service(ce_state->ar, CE_POLL_PIPE);
+       ath10k_snoc_process_rx_cb(ce_state, ath10k_snoc_htt_rx_deliver);
+}
+
+static void ath10k_snoc_rx_replenish_retry(struct timer_list *t)
+{
+       struct ath10k_pci *ar_snoc = from_timer(ar_snoc, t, rx_post_retry);
+       struct ath10k *ar = ar_snoc->ar;
+
+       ath10k_snoc_rx_post(ar);
+}
+
+static void ath10k_snoc_htc_tx_cb(struct ath10k_ce_pipe *ce_state)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct sk_buff_head list;
+       struct sk_buff *skb;
+
+       __skb_queue_head_init(&list);
+       while (ath10k_ce_completed_send_next(ce_state, (void **)&skb) == 0) {
+               if (!skb)
+                       continue;
+
+               __skb_queue_tail(&list, skb);
+       }
+
+       while ((skb = __skb_dequeue(&list)))
+               ath10k_htc_tx_completion_handler(ar, skb);
+}
+
+static void ath10k_snoc_htt_tx_cb(struct ath10k_ce_pipe *ce_state)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct sk_buff *skb;
+
+       while (ath10k_ce_completed_send_next(ce_state, (void **)&skb) == 0) {
+               if (!skb)
+                       continue;
+
+               dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
+                                skb->len, DMA_TO_DEVICE);
+               ath10k_htt_hif_tx_complete(ar, skb);
+       }
+}
+
+static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
+                                struct ath10k_hif_sg_item *items, int n_items)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct ath10k_ce *ce = ath10k_ce_priv(ar);
+       struct ath10k_snoc_pipe *snoc_pipe;
+       struct ath10k_ce_pipe *ce_pipe;
+       int err, i = 0;
+
+       snoc_pipe = &ar_snoc->pipe_info[pipe_id];
+       ce_pipe = snoc_pipe->ce_hdl;
+       spin_lock_bh(&ce->ce_lock);
+
+       for (i = 0; i < n_items - 1; i++) {
+               ath10k_dbg(ar, ATH10K_DBG_SNOC,
+                          "snoc tx item %d paddr %pad len %d n_items %d\n",
+                          i, &items[i].paddr, items[i].len, n_items);
+
+               err = ath10k_ce_send_nolock(ce_pipe,
+                                           items[i].transfer_context,
+                                           items[i].paddr,
+                                           items[i].len,
+                                           items[i].transfer_id,
+                                           CE_SEND_FLAG_GATHER);
+               if (err)
+                       goto err;
+       }
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC,
+                  "snoc tx item %d paddr %pad len %d n_items %d\n",
+                  i, &items[i].paddr, items[i].len, n_items);
+
+       err = ath10k_ce_send_nolock(ce_pipe,
+                                   items[i].transfer_context,
+                                   items[i].paddr,
+                                   items[i].len,
+                                   items[i].transfer_id,
+                                   0);
+       if (err)
+               goto err;
+
+       spin_unlock_bh(&ce->ce_lock);
+
+       return 0;
+
+err:
+       for (; i > 0; i--)
+               __ath10k_ce_send_revert(ce_pipe);
+
+       spin_unlock_bh(&ce->ce_lock);
+       return err;
+}
+
+static int ath10k_snoc_hif_get_target_info(struct ath10k *ar,
+                                          struct bmi_target_info *target_info)
+{
+       target_info->version = ATH10K_HW_WCN3990;
+       target_info->type = ATH10K_HW_WCN3990;
+
+       return 0;
+}
+
+static u16 ath10k_snoc_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "hif get free queue number\n");
+
+       return ath10k_ce_num_free_src_entries(ar_snoc->pipe_info[pipe].ce_hdl);
+}
+
+static void ath10k_snoc_hif_send_complete_check(struct ath10k *ar, u8 pipe,
+                                               int force)
+{
+       int resources;
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc hif send complete check\n");
+
+       if (!force) {
+               resources = ath10k_snoc_hif_get_free_queue_number(ar, pipe);
+
+               if (resources > (host_ce_config_wlan[pipe].src_nentries >> 1))
+                       return;
+       }
+       ath10k_ce_per_engine_service(ar, pipe);
+}
+
+static int ath10k_snoc_hif_map_service_to_pipe(struct ath10k *ar,
+                                              u16 service_id,
+                                              u8 *ul_pipe, u8 *dl_pipe)
+{
+       const struct service_to_pipe *entry;
+       bool ul_set = false, dl_set = false;
+       int i;
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc hif map service\n");
+
+       for (i = 0; i < ARRAY_SIZE(target_service_to_ce_map_wlan); i++) {
+               entry = &target_service_to_ce_map_wlan[i];
+
+               if (__le32_to_cpu(entry->service_id) != service_id)
+                       continue;
+
+               switch (__le32_to_cpu(entry->pipedir)) {
+               case PIPEDIR_NONE:
+                       break;
+               case PIPEDIR_IN:
+                       WARN_ON(dl_set);
+                       *dl_pipe = __le32_to_cpu(entry->pipenum);
+                       dl_set = true;
+                       break;
+               case PIPEDIR_OUT:
+                       WARN_ON(ul_set);
+                       *ul_pipe = __le32_to_cpu(entry->pipenum);
+                       ul_set = true;
+                       break;
+               case PIPEDIR_INOUT:
+                       WARN_ON(dl_set);
+                       WARN_ON(ul_set);
+                       *dl_pipe = __le32_to_cpu(entry->pipenum);
+                       *ul_pipe = __le32_to_cpu(entry->pipenum);
+                       dl_set = true;
+                       ul_set = true;
+                       break;
+               }
+       }
+
+       if (WARN_ON(!ul_set || !dl_set))
+               return -ENOENT;
+
+       return 0;
+}
+
+static void ath10k_snoc_hif_get_default_pipe(struct ath10k *ar,
+                                            u8 *ul_pipe, u8 *dl_pipe)
+{
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc hif get default pipe\n");
+
+       (void)ath10k_snoc_hif_map_service_to_pipe(ar,
+                                                ATH10K_HTC_SVC_ID_RSVD_CTRL,
+                                                ul_pipe, dl_pipe);
+}
+
+static inline void ath10k_snoc_irq_disable(struct ath10k *ar)
+{
+       ath10k_ce_disable_interrupts(ar);
+}
+
+static inline void ath10k_snoc_irq_enable(struct ath10k *ar)
+{
+       ath10k_ce_enable_interrupts(ar);
+}
+
+static void ath10k_snoc_rx_pipe_cleanup(struct ath10k_snoc_pipe *snoc_pipe)
+{
+       struct ath10k_ce_pipe *ce_pipe;
+       struct ath10k_ce_ring *ce_ring;
+       struct sk_buff *skb;
+       struct ath10k *ar;
+       int i;
+
+       ar = snoc_pipe->hif_ce_state;
+       ce_pipe = snoc_pipe->ce_hdl;
+       ce_ring = ce_pipe->dest_ring;
+
+       if (!ce_ring)
+               return;
+
+       if (!snoc_pipe->buf_sz)
+               return;
+
+       for (i = 0; i < ce_ring->nentries; i++) {
+               skb = ce_ring->per_transfer_context[i];
+               if (!skb)
+                       continue;
+
+               ce_ring->per_transfer_context[i] = NULL;
+
+               dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(skb);
+       }
+}
+
+static void ath10k_snoc_tx_pipe_cleanup(struct ath10k_snoc_pipe *snoc_pipe)
+{
+       struct ath10k_ce_pipe *ce_pipe;
+       struct ath10k_ce_ring *ce_ring;
+       struct ath10k_snoc *ar_snoc;
+       struct sk_buff *skb;
+       struct ath10k *ar;
+       int i;
+
+       ar = snoc_pipe->hif_ce_state;
+       ar_snoc = ath10k_snoc_priv(ar);
+       ce_pipe = snoc_pipe->ce_hdl;
+       ce_ring = ce_pipe->src_ring;
+
+       if (!ce_ring)
+               return;
+
+       if (!snoc_pipe->buf_sz)
+               return;
+
+       for (i = 0; i < ce_ring->nentries; i++) {
+               skb = ce_ring->per_transfer_context[i];
+               if (!skb)
+                       continue;
+
+               ce_ring->per_transfer_context[i] = NULL;
+
+               ath10k_htc_tx_completion_handler(ar, skb);
+       }
+}
+
+static void ath10k_snoc_buffer_cleanup(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct ath10k_snoc_pipe *pipe_info;
+       int pipe_num;
+
+       del_timer_sync(&ar_snoc->rx_post_retry);
+       for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
+               pipe_info = &ar_snoc->pipe_info[pipe_num];
+               ath10k_snoc_rx_pipe_cleanup(pipe_info);
+               ath10k_snoc_tx_pipe_cleanup(pipe_info);
+       }
+}
+
+static void ath10k_snoc_hif_stop(struct ath10k *ar)
+{
+       ath10k_snoc_irq_disable(ar);
+       ath10k_snoc_buffer_cleanup(ar);
+       napi_synchronize(&ar->napi);
+       napi_disable(&ar->napi);
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
+}
+
+static int ath10k_snoc_hif_start(struct ath10k *ar)
+{
+       ath10k_snoc_irq_enable(ar);
+       ath10k_snoc_rx_post(ar);
+
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
+
+       return 0;
+}
+
+static int ath10k_snoc_init_pipes(struct ath10k *ar)
+{
+       int i, ret;
+
+       for (i = 0; i < CE_COUNT; i++) {
+               ret = ath10k_ce_init_pipe(ar, i, &host_ce_config_wlan[i]);
+               if (ret) {
+                       ath10k_err(ar, "failed to initialize copy engine pipe %d: %d\n",
+                                  i, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int ath10k_snoc_wlan_enable(struct ath10k *ar)
+{
+       return 0;
+}
+
+static void ath10k_snoc_wlan_disable(struct ath10k *ar)
+{
+}
+
+static void ath10k_snoc_hif_power_down(struct ath10k *ar)
+{
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power down\n");
+
+       ath10k_snoc_wlan_disable(ar);
+       ath10k_ce_free_rri(ar);
+}
+
+static int ath10k_snoc_hif_power_up(struct ath10k *ar)
+{
+       int ret;
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 driver state = %d\n",
+                  __func__, ar->state);
+
+       ret = ath10k_snoc_wlan_enable(ar);
+       if (ret) {
+               ath10k_err(ar, "failed to enable wcn3990: %d\n", ret);
+               return ret;
+       }
+
+       ath10k_ce_alloc_rri(ar);
+
+       ret = ath10k_snoc_init_pipes(ar);
+       if (ret) {
+               ath10k_err(ar, "failed to initialize CE: %d\n", ret);
+               goto err_wlan_enable;
+       }
+
+       napi_enable(&ar->napi);
+       return 0;
+
+err_wlan_enable:
+       ath10k_snoc_wlan_disable(ar);
+
+       return ret;
+}
+
+static const struct ath10k_hif_ops ath10k_snoc_hif_ops = {
+       .read32         = ath10k_snoc_read32,
+       .write32        = ath10k_snoc_write32,
+       .start          = ath10k_snoc_hif_start,
+       .stop           = ath10k_snoc_hif_stop,
+       .map_service_to_pipe    = ath10k_snoc_hif_map_service_to_pipe,
+       .get_default_pipe       = ath10k_snoc_hif_get_default_pipe,
+       .power_up               = ath10k_snoc_hif_power_up,
+       .power_down             = ath10k_snoc_hif_power_down,
+       .tx_sg                  = ath10k_snoc_hif_tx_sg,
+       .send_complete_check    = ath10k_snoc_hif_send_complete_check,
+       .get_free_queue_number  = ath10k_snoc_hif_get_free_queue_number,
+       .get_target_info        = ath10k_snoc_hif_get_target_info,
+};
+
+static const struct ath10k_bus_ops ath10k_snoc_bus_ops = {
+       .read32         = ath10k_snoc_read32,
+       .write32        = ath10k_snoc_write32,
+};
+
+int ath10k_snoc_get_ce_id_from_irq(struct ath10k *ar, int irq)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       int i;
+
+       for (i = 0; i < CE_COUNT_MAX; i++) {
+               if (ar_snoc->ce_irqs[i].irq_line == irq)
+                       return i;
+       }
+       ath10k_err(ar, "No matching CE id for irq %d\n", irq);
+
+       return -EINVAL;
+}
+
+static irqreturn_t ath10k_snoc_per_engine_handler(int irq, void *arg)
+{
+       struct ath10k *ar = arg;
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       int ce_id = ath10k_snoc_get_ce_id_from_irq(ar, irq);
+
+       if (ce_id < 0 || ce_id >= ARRAY_SIZE(ar_snoc->pipe_info)) {
+               ath10k_warn(ar, "unexpected/invalid irq %d ce_id %d\n", irq,
+                           ce_id);
+               return IRQ_HANDLED;
+       }
+
+       ath10k_snoc_irq_disable(ar);
+       napi_schedule(&ar->napi);
+
+       return IRQ_HANDLED;
+}
+
+static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget)
+{
+       struct ath10k *ar = container_of(ctx, struct ath10k, napi);
+       int done = 0;
+
+       ath10k_ce_per_engine_service_any(ar);
+       done = ath10k_htt_txrx_compl_task(ar, budget);
+
+       if (done < budget) {
+               napi_complete(ctx);
+               ath10k_snoc_irq_enable(ar);
+       }
+
+       return done;
+}
+
+void ath10k_snoc_init_napi(struct ath10k *ar)
+{
+       netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll,
+                      ATH10K_NAPI_BUDGET);
+}
+
+static int ath10k_snoc_request_irq(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       int irqflags = IRQF_TRIGGER_RISING;
+       int ret, id;
+
+       for (id = 0; id < CE_COUNT_MAX; id++) {
+               ret = request_irq(ar_snoc->ce_irqs[id].irq_line,
+                                 ath10k_snoc_per_engine_handler,
+                                 irqflags, ce_name[id], ar);
+               if (ret) {
+                       ath10k_err(ar,
+                                  "failed to register IRQ handler for CE %d: %d",
+                                  id, ret);
+                       goto err_irq;
+               }
+       }
+
+       return 0;
+
+err_irq:
+       for (id -= 1; id >= 0; id--)
+               free_irq(ar_snoc->ce_irqs[id].irq_line, ar);
+
+       return ret;
+}
+
+static void ath10k_snoc_free_irq(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       int id;
+
+       for (id = 0; id < CE_COUNT_MAX; id++)
+               free_irq(ar_snoc->ce_irqs[id].irq_line, ar);
+}
+
+static int ath10k_snoc_resource_init(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct platform_device *pdev;
+       struct resource *res;
+       int i, ret = 0;
+
+       pdev = ar_snoc->dev;
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
+       if (!res) {
+               ath10k_err(ar, "Memory base not found in DT\n");
+               return -EINVAL;
+       }
+
+       ar_snoc->mem_pa = res->start;
+       ar_snoc->mem = devm_ioremap(&pdev->dev, ar_snoc->mem_pa,
+                                   resource_size(res));
+       if (!ar_snoc->mem) {
+               ath10k_err(ar, "Memory base ioremap failed with physical address %pa\n",
+                          &ar_snoc->mem_pa);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < CE_COUNT; i++) {
+               res = platform_get_resource(ar_snoc->dev, IORESOURCE_IRQ, i);
+               if (!res) {
+                       ath10k_err(ar, "failed to get IRQ%d\n", i);
+                       ret = -ENODEV;
+                       goto out;
+               }
+               ar_snoc->ce_irqs[i].irq_line = res->start;
+       }
+
+out:
+       return ret;
+}
+
+static int ath10k_snoc_setup_resource(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct ath10k_ce *ce = ath10k_ce_priv(ar);
+       struct ath10k_snoc_pipe *pipe;
+       int i, ret;
+
+       timer_setup(&ar_snoc->rx_post_retry, ath10k_snoc_rx_replenish_retry, 0);
+       spin_lock_init(&ce->ce_lock);
+       for (i = 0; i < CE_COUNT; i++) {
+               pipe = &ar_snoc->pipe_info[i];
+               pipe->ce_hdl = &ce->ce_states[i];
+               pipe->pipe_num = i;
+               pipe->hif_ce_state = ar;
+
+               ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]);
+               if (ret) {
+                       ath10k_err(ar, "failed to allocate copy engine pipe %d: %d\n",
+                                  i, ret);
+                       return ret;
+               }
+
+               pipe->buf_sz = host_ce_config_wlan[i].src_sz_max;
+       }
+       ath10k_snoc_init_napi(ar);
+
+       return 0;
+}
+
+static void ath10k_snoc_release_resource(struct ath10k *ar)
+{
+       int i;
+
+       netif_napi_del(&ar->napi);
+       for (i = 0; i < CE_COUNT; i++)
+               ath10k_ce_free_pipe(ar, i);
+}
+
+static int ath10k_get_vreg_info(struct ath10k *ar, struct device *dev,
+                               struct ath10k_wcn3990_vreg_info *vreg_info)
+{
+       struct regulator *reg;
+       int ret = 0;
+
+       reg = devm_regulator_get_optional(dev, vreg_info->name);
+
+       if (IS_ERR(reg)) {
+               ret = PTR_ERR(reg);
+
+               if (ret  == -EPROBE_DEFER) {
+                       ath10k_err(ar, "EPROBE_DEFER for regulator: %s\n",
+                                  vreg_info->name);
+                       return ret;
+               }
+               if (vreg_info->required) {
+                       ath10k_err(ar, "Regulator %s doesn't exist: %d\n",
+                                  vreg_info->name, ret);
+                       return ret;
+               }
+               ath10k_dbg(ar, ATH10K_DBG_SNOC,
+                          "Optional regulator %s doesn't exist: %d\n",
+                          vreg_info->name, ret);
+               goto done;
+       }
+
+       vreg_info->reg = reg;
+
+done:
+       ath10k_dbg(ar, ATH10K_DBG_SNOC,
+                  "snog vreg %s min_v %u max_v %u load_ua %u settle_delay %lu\n",
+                  vreg_info->name, vreg_info->min_v, vreg_info->max_v,
+                  vreg_info->load_ua, vreg_info->settle_delay);
+
+       return 0;
+}
+
+static int ath10k_get_clk_info(struct ath10k *ar, struct device *dev,
+                              struct ath10k_wcn3990_clk_info *clk_info)
+{
+       struct clk *handle;
+       int ret = 0;
+
+       handle = devm_clk_get(dev, clk_info->name);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               if (clk_info->required) {
+                       ath10k_err(ar, "snoc clock %s isn't available: %d\n",
+                                  clk_info->name, ret);
+                       return ret;
+               }
+               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc ignoring clock %s: %d\n",
+                          clk_info->name,
+                          ret);
+               return 0;
+       }
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc clock %s freq %u\n",
+                  clk_info->name, clk_info->freq);
+
+       clk_info->handle = handle;
+
+       return ret;
+}
+
+static int ath10k_wcn3990_vreg_on(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct ath10k_wcn3990_vreg_info *vreg_info;
+       int ret = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(vreg_cfg); i++) {
+               vreg_info = &ar_snoc->vreg[i];
+
+               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);
+                       goto err_reg_config;
+               }
+
+               if (vreg_info->settle_delay)
+                       udelay(vreg_info->settle_delay);
+       }
+
+       return 0;
+
+err_reg_config:
+       for (; 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);
+       }
+
+       return ret;
+}
+
+static int ath10k_wcn3990_vreg_off(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct ath10k_wcn3990_vreg_info *vreg_info;
+       int ret = 0;
+       int i;
+
+       for (i = ARRAY_SIZE(vreg_cfg) - 1; i >= 0; i--) {
+               vreg_info = &ar_snoc->vreg[i];
+
+               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);
+       }
+
+       return ret;
+}
+
+static int ath10k_wcn3990_clk_init(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct ath10k_wcn3990_clk_info *clk_info;
+       int ret = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(clk_cfg); i++) {
+               clk_info = &ar_snoc->clk[i];
+
+               if (!clk_info->handle)
+                       continue;
+
+               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc clock %s being enabled\n",
+                          clk_info->name);
+
+               if (clk_info->freq) {
+                       ret = clk_set_rate(clk_info->handle, clk_info->freq);
+
+                       if (ret) {
+                               ath10k_err(ar, "failed to set clock %s freq %u\n",
+                                          clk_info->name, clk_info->freq);
+                               goto err_clock_config;
+                       }
+               }
+
+               ret = clk_prepare_enable(clk_info->handle);
+               if (ret) {
+                       ath10k_err(ar, "failed to enable clock %s\n",
+                                  clk_info->name);
+                       goto err_clock_config;
+               }
+       }
+
+       return 0;
+
+err_clock_config:
+       for (; i >= 0; i--) {
+               clk_info = &ar_snoc->clk[i];
+
+               if (!clk_info->handle)
+                       continue;
+
+               clk_disable_unprepare(clk_info->handle);
+       }
+
+       return ret;
+}
+
+static int ath10k_wcn3990_clk_deinit(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct ath10k_wcn3990_clk_info *clk_info;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(clk_cfg); i++) {
+               clk_info = &ar_snoc->clk[i];
+
+               if (!clk_info->handle)
+                       continue;
+
+               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc clock %s being disabled\n",
+                          clk_info->name);
+
+               clk_disable_unprepare(clk_info->handle);
+       }
+
+       return 0;
+}
+
+static int ath10k_hw_power_on(struct ath10k *ar)
+{
+       int ret;
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power on\n");
+
+       ret = ath10k_wcn3990_vreg_on(ar);
+       if (ret)
+               return ret;
+
+       ret = ath10k_wcn3990_clk_init(ar);
+       if (ret)
+               goto vreg_off;
+
+       return ret;
+
+vreg_off:
+       ath10k_wcn3990_vreg_off(ar);
+       return ret;
+}
+
+static int ath10k_hw_power_off(struct ath10k *ar)
+{
+       int ret;
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power off\n");
+
+       ath10k_wcn3990_clk_deinit(ar);
+
+       ret = ath10k_wcn3990_vreg_off(ar);
+
+       return ret;
+}
+
+static const struct of_device_id ath10k_snoc_dt_match[] = {
+       { .compatible = "qcom,wcn3990-wifi",
+        .data = &drv_priv,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ath10k_snoc_dt_match);
+
+static int ath10k_snoc_probe(struct platform_device *pdev)
+{
+       const struct ath10k_snoc_drv_priv *drv_data;
+       const struct of_device_id *of_id;
+       struct ath10k_snoc *ar_snoc;
+       struct device *dev;
+       struct ath10k *ar;
+       int ret;
+       u32 i;
+
+       of_id = of_match_device(ath10k_snoc_dt_match, &pdev->dev);
+       if (!of_id) {
+               dev_err(&pdev->dev, "failed to find matching device tree id\n");
+               return -EINVAL;
+       }
+
+       drv_data = of_id->data;
+       dev = &pdev->dev;
+
+       ret = dma_set_mask_and_coherent(dev, drv_data->dma_mask);
+       if (ret) {
+               dev_err(dev, "failed to set dma mask: %d", ret);
+               return ret;
+       }
+
+       ar = ath10k_core_create(sizeof(*ar_snoc), dev, ATH10K_BUS_SNOC,
+                               drv_data->hw_rev, &ath10k_snoc_hif_ops);
+       if (!ar) {
+               dev_err(dev, "failed to allocate core\n");
+               return -ENOMEM;
+       }
+
+       ar_snoc = ath10k_snoc_priv(ar);
+       ar_snoc->dev = pdev;
+       platform_set_drvdata(pdev, ar);
+       ar_snoc->ar = ar;
+       ar_snoc->ce.bus_ops = &ath10k_snoc_bus_ops;
+       ar->ce_priv = &ar_snoc->ce;
+
+       ath10k_snoc_resource_init(ar);
+       if (ret) {
+               ath10k_warn(ar, "failed to initialize resource: %d\n", ret);
+               goto err_core_destroy;
+       }
+
+       ath10k_snoc_setup_resource(ar);
+       if (ret) {
+               ath10k_warn(ar, "failed to setup resource: %d\n", ret);
+               goto err_core_destroy;
+       }
+       ret = ath10k_snoc_request_irq(ar);
+       if (ret) {
+               ath10k_warn(ar, "failed to request irqs: %d\n", ret);
+               goto err_release_resource;
+       }
+
+       ar_snoc->vreg = vreg_cfg;
+       for (i = 0; i < ARRAY_SIZE(vreg_cfg); i++) {
+               ret = ath10k_get_vreg_info(ar, dev, &ar_snoc->vreg[i]);
+               if (ret)
+                       goto err_free_irq;
+       }
+
+       ar_snoc->clk = clk_cfg;
+       for (i = 0; i < ARRAY_SIZE(clk_cfg); i++) {
+               ret = ath10k_get_clk_info(ar, dev, &ar_snoc->clk[i]);
+               if (ret)
+                       goto err_free_irq;
+       }
+
+       ret = ath10k_hw_power_on(ar);
+       if (ret) {
+               ath10k_err(ar, "failed to power on device: %d\n", ret);
+               goto err_free_irq;
+       }
+
+       ret = ath10k_core_register(ar, drv_data->hw_rev);
+       if (ret) {
+               ath10k_err(ar, "failed to register driver core: %d\n", ret);
+               goto err_hw_power_off;
+       }
+
+       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;
+
+err_hw_power_off:
+       ath10k_hw_power_off(ar);
+
+err_free_irq:
+       ath10k_snoc_free_irq(ar);
+
+err_release_resource:
+       ath10k_snoc_release_resource(ar);
+
+err_core_destroy:
+       ath10k_core_destroy(ar);
+
+       return ret;
+}
+
+static int ath10k_snoc_remove(struct platform_device *pdev)
+{
+       struct ath10k *ar = platform_get_drvdata(pdev);
+
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc remove\n");
+       ath10k_core_unregister(ar);
+       ath10k_hw_power_off(ar);
+       ath10k_snoc_free_irq(ar);
+       ath10k_snoc_release_resource(ar);
+       ath10k_core_destroy(ar);
+
+       return 0;
+}
+
+static struct platform_driver ath10k_snoc_driver = {
+               .probe  = ath10k_snoc_probe,
+               .remove = ath10k_snoc_remove,
+               .driver = {
+                       .name   = "ath10k_snoc",
+                       .owner = THIS_MODULE,
+                       .of_match_table = ath10k_snoc_dt_match,
+               },
+};
+
+static int __init ath10k_snoc_init(void)
+{
+       int ret;
+
+       ret = platform_driver_register(&ath10k_snoc_driver);
+       if (ret)
+               pr_err("failed to register ath10k snoc driver: %d\n",
+                      ret);
+
+       return ret;
+}
+module_init(ath10k_snoc_init);
+
+static void __exit ath10k_snoc_exit(void)
+{
+       platform_driver_unregister(&ath10k_snoc_driver);
+}
+module_exit(ath10k_snoc_exit);
+
+MODULE_AUTHOR("Qualcomm");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Driver support for Atheros WCN3990 SNOC devices");
diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h
new file mode 100644 (file)
index 0000000..05dc98f
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ *
+ * 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 _SNOC_H_
+#define _SNOC_H_
+
+#include "hw.h"
+#include "ce.h"
+#include "pci.h"
+
+struct ath10k_snoc_drv_priv {
+       enum ath10k_hw_rev hw_rev;
+       u64 dma_mask;
+};
+
+struct snoc_state {
+       u32 pipe_cfg_addr;
+       u32 svc_to_pipe_map;
+};
+
+struct ath10k_snoc_pipe {
+       struct ath10k_ce_pipe *ce_hdl;
+       u8 pipe_num;
+       struct ath10k *hif_ce_state;
+       size_t buf_sz;
+       /* protect ce info */
+       spinlock_t pipe_lock;
+       struct ath10k_snoc *ar_snoc;
+};
+
+struct ath10k_snoc_target_info {
+       u32 target_version;
+       u32 target_type;
+       u32 target_revision;
+       u32 soc_version;
+};
+
+struct ath10k_snoc_ce_irq {
+       u32 irq_line;
+};
+
+struct ath10k_wcn3990_vreg_info {
+       struct regulator *reg;
+       const char *name;
+       u32 min_v;
+       u32 max_v;
+       u32 load_ua;
+       unsigned long settle_delay;
+       bool required;
+};
+
+struct ath10k_wcn3990_clk_info {
+       struct clk *handle;
+       const char *name;
+       u32 freq;
+       bool required;
+};
+
+struct ath10k_snoc {
+       struct platform_device *dev;
+       struct ath10k *ar;
+       void __iomem *mem;
+       dma_addr_t mem_pa;
+       struct ath10k_snoc_target_info target_info;
+       size_t mem_len;
+       struct ath10k_snoc_pipe pipe_info[CE_COUNT_MAX];
+       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;
+};
+
+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);
+
+#endif /* _SNOC_H_ */
index 70e23bb..cda164f 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2016 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -119,6 +120,13 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
                        info->flags &= ~IEEE80211_TX_STAT_ACK;
        }
 
+       if (tx_done->status == HTT_TX_COMPL_STATE_ACK &&
+           tx_done->ack_rssi != ATH10K_INVALID_RSSI) {
+               info->status.ack_signal = ATH10K_DEFAULT_NOISE_FLOOR +
+                                               tx_done->ack_rssi;
+               info->status.is_valid_ack_signal = true;
+       }
+
        ieee80211_tx_status(htt->ar->hw, msdu);
        /* we do not own the msdu anymore */
 
index c35e453..e37d16b 100644 (file)
@@ -25,6 +25,7 @@ struct sk_buff;
 struct wmi_ops {
        void (*rx)(struct ath10k *ar, struct sk_buff *skb);
        void (*map_svc)(const __le32 *in, unsigned long *out, size_t len);
+       void (*map_svc_ext)(const __le32 *in, unsigned long *out, size_t len);
 
        int (*pull_scan)(struct ath10k *ar, struct sk_buff *skb,
                         struct wmi_scan_ev_arg *arg);
@@ -54,6 +55,9 @@ struct wmi_ops {
                              struct wmi_wow_ev_arg *arg);
        int (*pull_echo_ev)(struct ath10k *ar, struct sk_buff *skb,
                            struct wmi_echo_ev_arg *arg);
+       int (*pull_svc_avail)(struct ath10k *ar, struct sk_buff *skb,
+                             struct wmi_svc_avail_ev_arg *arg);
+
        enum wmi_txbf_conf (*get_txbf_conf_scheme)(struct ath10k *ar);
 
        struct sk_buff *(*gen_pdev_suspend)(struct ath10k *ar, u32 suspend_opt);
@@ -115,6 +119,8 @@ struct wmi_ops {
                                         u32 value);
        struct sk_buff *(*gen_scan_chan_list)(struct ath10k *ar,
                                              const struct wmi_scan_chan_list_arg *arg);
+       struct sk_buff *(*gen_scan_prob_req_oui)(struct ath10k *ar,
+                                                u32 prob_req_oui);
        struct sk_buff *(*gen_beacon_dma)(struct ath10k *ar, u32 vdev_id,
                                          const void *bcn, size_t bcn_len,
                                          u32 bcn_paddr, bool dtim_zero,
@@ -229,6 +235,17 @@ ath10k_wmi_map_svc(struct ath10k *ar, const __le32 *in, unsigned long *out,
        return 0;
 }
 
+static inline int
+ath10k_wmi_map_svc_ext(struct ath10k *ar, const __le32 *in, unsigned long *out,
+                      size_t len)
+{
+       if (!ar->wmi.ops->map_svc_ext)
+               return -EOPNOTSUPP;
+
+       ar->wmi.ops->map_svc_ext(in, out, len);
+       return 0;
+}
+
 static inline int
 ath10k_wmi_pull_scan(struct ath10k *ar, struct sk_buff *skb,
                     struct wmi_scan_ev_arg *arg)
@@ -329,6 +346,15 @@ ath10k_wmi_pull_rdy(struct ath10k *ar, struct sk_buff *skb,
        return ar->wmi.ops->pull_rdy(ar, skb, arg);
 }
 
+static inline int
+ath10k_wmi_pull_svc_avail(struct ath10k *ar, struct sk_buff *skb,
+                         struct wmi_svc_avail_ev_arg *arg)
+{
+       if (!ar->wmi.ops->pull_svc_avail)
+               return -EOPNOTSUPP;
+       return ar->wmi.ops->pull_svc_avail(ar, skb, arg);
+}
+
 static inline int
 ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
                         struct ath10k_fw_stats *stats)
@@ -890,6 +916,26 @@ ath10k_wmi_scan_chan_list(struct ath10k *ar,
        return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
 }
 
+static inline int
+ath10k_wmi_scan_prob_req_oui(struct ath10k *ar, const u8 mac_addr[ETH_ALEN])
+{
+       struct sk_buff *skb;
+       u32 prob_req_oui;
+
+       prob_req_oui = (((u32)mac_addr[0]) << 16) |
+                      (((u32)mac_addr[1]) << 8) | mac_addr[2];
+
+       if (!ar->wmi.ops->gen_scan_prob_req_oui)
+               return -EOPNOTSUPP;
+
+       skb = ar->wmi.ops->gen_scan_prob_req_oui(ar, prob_req_oui);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       return ath10k_wmi_cmd_send(ar, skb,
+                       ar->wmi.cmd->scan_prob_req_oui_cmdid);
+}
+
 static inline int
 ath10k_wmi_peer_assoc(struct ath10k *ar,
                      const struct wmi_peer_assoc_complete_arg *arg)
index 9d1b0a4..01f4eb2 100644 (file)
@@ -594,6 +594,9 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
        case WMI_TLV_READY_EVENTID:
                ath10k_wmi_event_ready(ar, skb);
                break;
+       case WMI_TLV_SERVICE_AVAILABLE_EVENTID:
+               ath10k_wmi_event_service_available(ar, skb);
+               break;
        case WMI_TLV_OFFLOAD_BCN_TX_STATUS_EVENTID:
                ath10k_wmi_tlv_event_bcn_tx_status(ar, skb);
                break;
@@ -1117,6 +1120,39 @@ static int ath10k_wmi_tlv_op_pull_rdy_ev(struct ath10k *ar,
        return 0;
 }
 
+static int ath10k_wmi_tlv_svc_avail_parse(struct ath10k *ar, u16 tag, u16 len,
+                                         const void *ptr, void *data)
+{
+       struct wmi_svc_avail_ev_arg *arg = data;
+
+       switch (tag) {
+       case WMI_TLV_TAG_STRUCT_SERVICE_AVAILABLE_EVENT:
+               arg->service_map_ext_len = *(__le32 *)ptr;
+               arg->service_map_ext = ptr + sizeof(__le32);
+               return 0;
+       default:
+               break;
+       }
+       return -EPROTO;
+}
+
+static int ath10k_wmi_tlv_op_pull_svc_avail(struct ath10k *ar,
+                                           struct sk_buff *skb,
+                                           struct wmi_svc_avail_ev_arg *arg)
+{
+       int ret;
+
+       ret = ath10k_wmi_tlv_iter(ar, skb->data, skb->len,
+                                 ath10k_wmi_tlv_svc_avail_parse, arg);
+
+       if (ret) {
+               ath10k_warn(ar, "failed to parse svc_avail tlv: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static void ath10k_wmi_tlv_pull_vdev_stats(const struct wmi_tlv_vdev_stats *src,
                                           struct ath10k_fw_stats_vdev *dst)
 {
@@ -1600,6 +1636,8 @@ ath10k_wmi_tlv_op_gen_start_scan(struct ath10k *ar,
        cmd->num_bssids = __cpu_to_le32(arg->n_bssids);
        cmd->ie_len = __cpu_to_le32(arg->ie_len);
        cmd->num_probes = __cpu_to_le32(3);
+       ether_addr_copy(cmd->mac_addr.addr, arg->mac_addr.addr);
+       ether_addr_copy(cmd->mac_mask.addr, arg->mac_mask.addr);
 
        /* FIXME: There are some scan flag inconsistencies across firmwares,
         * e.g. WMI-TLV inverts the logic behind the following flag.
@@ -2446,6 +2484,27 @@ ath10k_wmi_tlv_op_gen_scan_chan_list(struct ath10k *ar,
        return skb;
 }
 
+static struct sk_buff *
+ath10k_wmi_tlv_op_gen_scan_prob_req_oui(struct ath10k *ar, u32 prob_req_oui)
+{
+       struct wmi_scan_prob_req_oui_cmd *cmd;
+       struct wmi_tlv *tlv;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
+
+       tlv = (void *)skb->data;
+       tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_SCAN_PROB_REQ_OUI_CMD);
+       tlv->len = __cpu_to_le16(sizeof(*cmd));
+       cmd = (void *)tlv->value;
+       cmd->prob_req_oui = __cpu_to_le32(prob_req_oui);
+
+       ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv scan prob req oui\n");
+       return skb;
+}
+
 static struct sk_buff *
 ath10k_wmi_tlv_op_gen_beacon_dma(struct ath10k *ar, u32 vdev_id,
                                 const void *bcn, size_t bcn_len,
@@ -3416,6 +3475,7 @@ static struct wmi_cmd_map wmi_tlv_cmd_map = {
        .stop_scan_cmdid = WMI_TLV_STOP_SCAN_CMDID,
        .scan_chan_list_cmdid = WMI_TLV_SCAN_CHAN_LIST_CMDID,
        .scan_sch_prio_tbl_cmdid = WMI_TLV_SCAN_SCH_PRIO_TBL_CMDID,
+       .scan_prob_req_oui_cmdid = WMI_TLV_SCAN_PROB_REQ_OUI_CMDID,
        .pdev_set_regdomain_cmdid = WMI_TLV_PDEV_SET_REGDOMAIN_CMDID,
        .pdev_set_channel_cmdid = WMI_TLV_PDEV_SET_CHANNEL_CMDID,
        .pdev_set_param_cmdid = WMI_TLV_PDEV_SET_PARAM_CMDID,
@@ -3740,6 +3800,7 @@ static struct wmi_vdev_param_map wmi_tlv_vdev_param_map = {
 static const struct wmi_ops wmi_tlv_ops = {
        .rx = ath10k_wmi_tlv_op_rx,
        .map_svc = wmi_tlv_svc_map,
+       .map_svc_ext = wmi_tlv_svc_map_ext,
 
        .pull_scan = ath10k_wmi_tlv_op_pull_scan_ev,
        .pull_mgmt_rx = ath10k_wmi_tlv_op_pull_mgmt_rx_ev,
@@ -3751,6 +3812,7 @@ static const struct wmi_ops wmi_tlv_ops = {
        .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
        .pull_svc_rdy = ath10k_wmi_tlv_op_pull_svc_rdy_ev,
        .pull_rdy = ath10k_wmi_tlv_op_pull_rdy_ev,
+       .pull_svc_avail = ath10k_wmi_tlv_op_pull_svc_avail,
        .pull_fw_stats = ath10k_wmi_tlv_op_pull_fw_stats,
        .pull_roam_ev = ath10k_wmi_tlv_op_pull_roam_ev,
        .pull_wow_event = ath10k_wmi_tlv_op_pull_wow_ev,
@@ -3782,6 +3844,7 @@ static const struct wmi_ops wmi_tlv_ops = {
        .gen_set_sta_ps = ath10k_wmi_tlv_op_gen_set_sta_ps,
        .gen_set_ap_ps = ath10k_wmi_tlv_op_gen_set_ap_ps,
        .gen_scan_chan_list = ath10k_wmi_tlv_op_gen_scan_chan_list,
+       .gen_scan_prob_req_oui = ath10k_wmi_tlv_op_gen_scan_prob_req_oui,
        .gen_beacon_dma = ath10k_wmi_tlv_op_gen_beacon_dma,
        .gen_pdev_set_wmm = ath10k_wmi_tlv_op_gen_pdev_set_wmm,
        .gen_request_stats = ath10k_wmi_tlv_op_gen_request_stats,
index fa3773e..954c50b 100644 (file)
@@ -295,6 +295,7 @@ enum wmi_tlv_cmd_id {
 enum wmi_tlv_event_id {
        WMI_TLV_SERVICE_READY_EVENTID = 0x1,
        WMI_TLV_READY_EVENTID,
+       WMI_TLV_SERVICE_AVAILABLE_EVENTID,
        WMI_TLV_SCAN_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SCAN),
        WMI_TLV_PDEV_TPC_CONFIG_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PDEV),
        WMI_TLV_CHAN_INFO_EVENTID,
@@ -949,6 +950,275 @@ enum wmi_tlv_tag {
        WMI_TLV_TAG_STRUCT_PACKET_FILTER_ENABLE,
        WMI_TLV_TAG_STRUCT_SAP_SET_BLACKLIST_PARAM_CMD,
        WMI_TLV_TAG_STRUCT_MGMT_TX_CMD,
+       WMI_TLV_TAG_STRUCT_MGMT_TX_COMPL_EVENT,
+       WMI_TLV_TAG_STRUCT_SOC_SET_ANTENNA_MODE_CMD,
+       WMI_TLV_TAG_STRUCT_WOW_UDP_SVC_OFLD_CMD,
+       WMI_TLV_TAG_STRUCT_LRO_INFO_CMD,
+       WMI_TLV_TAG_STRUCT_ROAM_EARLYSTOP_RSSI_THRES_PARAM,
+       WMI_TLV_TAG_STRUCT_SERVICE_READY_EXT_EVENT,
+       WMI_TLV_TAG_STRUCT_MAWC_SENSOR_REPORT_IND_CMD,
+       WMI_TLV_TAG_STRUCT_MAWC_ENABLE_SENSOR_EVENT,
+       WMI_TLV_TAG_STRUCT_ROAM_CONFIGURE_MAWC_CMD,
+       WMI_TLV_TAG_STRUCT_NLO_CONFIGURE_MAWC_CMD,
+       WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_MAWC_CMD,
+       WMI_TLV_TAG_STRUCT_PEER_ASSOC_CONF_EVENT,
+       WMI_TLV_TAG_STRUCT_WOW_HOSTWAKEUP_GPIO_PIN_PATTERN_CONFIG_CMD,
+       WMI_TLV_TAG_STRUCT_AP_PS_EGAP_PARAM_CMD,
+       WMI_TLV_TAG_STRUCT_AP_PS_EGAP_INFO_EVENT,
+       WMI_TLV_TAG_STRUCT_PMF_OFFLOAD_SET_SA_QUERY_CMD,
+       WMI_TLV_TAG_STRUCT_TRANSFER_DATA_TO_FLASH_CMD,
+       WMI_TLV_TAG_STRUCT_TRANSFER_DATA_TO_FLASH_COMPLETE_EVENT,
+       WMI_TLV_TAG_STRUCT_SCPC_EVENT,
+       WMI_TLV_TAG_STRUCT_AP_PS_EGAP_INFO_CHAINMASK_LIST,
+       WMI_TLV_TAG_STRUCT_STA_SMPS_FORCE_MODE_COMPLETE_EVENT,
+       WMI_TLV_TAG_STRUCT_BPF_GET_CAPABILITY_CMD,
+       WMI_TLV_TAG_STRUCT_BPF_CAPABILITY_INFO_EVT,
+       WMI_TLV_TAG_STRUCT_BPF_GET_VDEV_STATS_CMD,
+       WMI_TLV_TAG_STRUCT_BPF_VDEV_STATS_INFO_EVT,
+       WMI_TLV_TAG_STRUCT_BPF_SET_VDEV_INSTRUCTIONS_CMD,
+       WMI_TLV_TAG_STRUCT_BPF_DEL_VDEV_INSTRUCTIONS_CMD,
+       WMI_TLV_TAG_STRUCT_VDEV_DELETE_RESP_EVENT,
+       WMI_TLV_TAG_STRUCT_PEER_DELETE_RESP_EVENT,
+       WMI_TLV_TAG_STRUCT_ROAM_DENSE_THRES_PARAM,
+       WMI_TLV_TAG_STRUCT_ENLO_CANDIDATE_SCORE_PARAM,
+       WMI_TLV_TAG_STRUCT_PEER_UPDATE_WDS_ENTRY_CMD,
+       WMI_TLV_TAG_STRUCT_VDEV_CONFIG_RATEMASK,
+       WMI_TLV_TAG_STRUCT_PDEV_FIPS_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SMART_ANT_ENABLE_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SMART_ANT_SET_RX_ANTENNA_CMD,
+       WMI_TLV_TAG_STRUCT_PEER_SMART_ANT_SET_TX_ANTENNA_CMD,
+       WMI_TLV_TAG_STRUCT_PEER_SMART_ANT_SET_TRAIN_ANTENNA_CMD,
+       WMI_TLV_TAG_STRUCT_PEER_SMART_ANT_SET_NODE_CONFIG_OPS_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_ANT_SWITCH_TBL_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_CTL_TABLE_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_MIMOGAIN_TABLE_CMD,
+       WMI_TLV_TAG_STRUCT_FWTEST_SET_PARAM_CMD,
+       WMI_TLV_TAG_STRUCT_PEER_ATF_REQUEST,
+       WMI_TLV_TAG_STRUCT_VDEV_ATF_REQUEST,
+       WMI_TLV_TAG_STRUCT_PDEV_GET_ANI_CCK_CONFIG_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_GET_ANI_OFDM_CONFIG_CMD,
+       WMI_TLV_TAG_STRUCT_INST_RSSI_STATS_RESP,
+       WMI_TLV_TAG_STRUCT_MED_UTIL_REPORT_EVENT,
+       WMI_TLV_TAG_STRUCT_PEER_STA_PS_STATECHANGE_EVENT,
+       WMI_TLV_TAG_STRUCT_WDS_ADDR_EVENT,
+       WMI_TLV_TAG_STRUCT_PEER_RATECODE_LIST_EVENT,
+       WMI_TLV_TAG_STRUCT_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENT,
+       WMI_TLV_TAG_STRUCT_PDEV_TPC_EVENT,
+       WMI_TLV_TAG_STRUCT_ANI_OFDM_EVENT,
+       WMI_TLV_TAG_STRUCT_ANI_CCK_EVENT,
+       WMI_TLV_TAG_STRUCT_PDEV_CHANNEL_HOPPING_EVENT,
+       WMI_TLV_TAG_STRUCT_PDEV_FIPS_EVENT,
+       WMI_TLV_TAG_STRUCT_ATF_PEER_INFO,
+       WMI_TLV_TAG_STRUCT_PDEV_GET_TPC_CMD,
+       WMI_TLV_TAG_STRUCT_VDEV_FILTER_NRP_CONFIG_CMD,
+       WMI_TLV_TAG_STRUCT_QBOOST_CFG_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SMART_ANT_GPIO_HANDLE,
+       WMI_TLV_TAG_STRUCT_PEER_SMART_ANT_SET_TX_ANTENNA_SERIES,
+       WMI_TLV_TAG_STRUCT_PEER_SMART_ANT_SET_TRAIN_ANTENNA_PARAM,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_ANT_CTRL_CHAIN,
+       WMI_TLV_TAG_STRUCT_PEER_CCK_OFDM_RATE_INFO,
+       WMI_TLV_TAG_STRUCT_PEER_MCS_RATE_INFO,
+       WMI_TLV_TAG_STRUCT_PDEV_NFCAL_POWER_ALL_CHANNELS_NFDBR,
+       WMI_TLV_TAG_STRUCT_PDEV_NFCAL_POWER_ALL_CHANNELS_NFDBM,
+       WMI_TLV_TAG_STRUCT_PDEV_NFCAL_POWER_ALL_CHANNELS_FREQNUM,
+       WMI_TLV_TAG_STRUCT_MU_REPORT_TOTAL_MU,
+       WMI_TLV_TAG_STRUCT_VDEV_SET_DSCP_TID_MAP_CMD,
+       WMI_TLV_TAG_STRUCT_ROAM_SET_MBO,
+       WMI_TLV_TAG_STRUCT_MIB_STATS_ENABLE_CMD,
+       WMI_TLV_TAG_STRUCT_NAN_DISC_IFACE_CREATED_EVENT,
+       WMI_TLV_TAG_STRUCT_NAN_DISC_IFACE_DELETED_EVENT,
+       WMI_TLV_TAG_STRUCT_NAN_STARTED_CLUSTER_EVENT,
+       WMI_TLV_TAG_STRUCT_NAN_JOINED_CLUSTER_EVENT,
+       WMI_TLV_TAG_STRUCT_NDI_GET_CAP_REQ,
+       WMI_TLV_TAG_STRUCT_NDP_INITIATOR_REQ,
+       WMI_TLV_TAG_STRUCT_NDP_RESPONDER_REQ,
+       WMI_TLV_TAG_STRUCT_NDP_END_REQ,
+       WMI_TLV_TAG_STRUCT_NDI_CAP_RSP_EVENT,
+       WMI_TLV_TAG_STRUCT_NDP_INITIATOR_RSP_EVENT,
+       WMI_TLV_TAG_STRUCT_NDP_RESPONDER_RSP_EVENT,
+       WMI_TLV_TAG_STRUCT_NDP_END_RSP_EVENT,
+       WMI_TLV_TAG_STRUCT_NDP_INDICATION_EVENT,
+       WMI_TLV_TAG_STRUCT_NDP_CONFIRM_EVENT,
+       WMI_TLV_TAG_STRUCT_NDP_END_INDICATION_EVENT,
+       WMI_TLV_TAG_STRUCT_VDEV_SET_QUIET_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_PCL_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_HW_MODE_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_MAC_CONFIG_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_ANTENNA_MODE_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_HW_MODE_RESPONSE_EVENT,
+       WMI_TLV_TAG_STRUCT_PDEV_HW_MODE_TRANSITION_EVENT,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_HW_MODE_RESPONSE_VDEV_MAC_ENTRY,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_MAC_CONFIG_RESPONSE_EVENT,
+       WMI_TLV_TAG_STRUCT_COEX_CONFIG_CMD,
+       WMI_TLV_TAG_STRUCT_CONFIG_ENHANCED_MCAST_FILTER,
+       WMI_TLV_TAG_STRUCT_CHAN_AVOID_RPT_ALLOW_CMD,
+       WMI_TLV_TAG_STRUCT_SET_PERIODIC_CHANNEL_STATS_CONFIG,
+       WMI_TLV_TAG_STRUCT_VDEV_SET_CUSTOM_AGGR_SIZE_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_WAL_POWER_DEBUG_CMD,
+       WMI_TLV_TAG_STRUCT_MAC_PHY_CAPABILITIES,
+       WMI_TLV_TAG_STRUCT_HW_MODE_CAPABILITIES,
+       WMI_TLV_TAG_STRUCT_SOC_MAC_PHY_HW_MODE_CAPS,
+       WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES_EXT,
+       WMI_TLV_TAG_STRUCT_SOC_HAL_REG_CAPABILITIES,
+       WMI_TLV_TAG_STRUCT_VDEV_WISA_CMD,
+       WMI_TLV_TAG_STRUCT_TX_POWER_LEVEL_STATS_EVT,
+       WMI_TLV_TAG_STRUCT_SCAN_ADAPTIVE_DWELL_PARAMETERS_TLV,
+       WMI_TLV_TAG_STRUCT_SCAN_ADAPTIVE_DWELL_CONFIG,
+       WMI_TLV_TAG_STRUCT_WOW_SET_ACTION_WAKE_UP_CMD,
+       WMI_TLV_TAG_STRUCT_NDP_END_RSP_PER_NDI,
+       WMI_TLV_TAG_STRUCT_PEER_BWF_REQUEST,
+       WMI_TLV_TAG_STRUCT_BWF_PEER_INFO,
+       WMI_TLV_TAG_STRUCT_DBGLOG_TIME_STAMP_SYNC_CMD,
+       WMI_TLV_TAG_STRUCT_RMC_SET_LEADER_CMD,
+       WMI_TLV_TAG_STRUCT_RMC_MANUAL_LEADER_EVENT,
+       WMI_TLV_TAG_STRUCT_PER_CHAIN_RSSI_STATS,
+       WMI_TLV_TAG_STRUCT_RSSI_STATS,
+       WMI_TLV_TAG_STRUCT_P2P_LO_START_CMD,
+       WMI_TLV_TAG_STRUCT_P2P_LO_STOP_CMD,
+       WMI_TLV_TAG_STRUCT_P2P_LO_STOPPED_EVENT,
+       WMI_TLV_TAG_STRUCT_PEER_REORDER_QUEUE_SETUP_CMD,
+       WMI_TLV_TAG_STRUCT_PEER_REORDER_QUEUE_REMOVE_CMD,
+       WMI_TLV_TAG_STRUCT_SET_MULTIPLE_MCAST_FILTER_CMD,
+       WMI_TLV_TAG_STRUCT_MGMT_TX_COMPL_BUNDLE_EVENT,
+       WMI_TLV_TAG_STRUCT_READ_DATA_FROM_FLASH_CMD,
+       WMI_TLV_TAG_STRUCT_READ_DATA_FROM_FLASH_EVENT,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_REORDER_TIMEOUT_VAL_CMD,
+       WMI_TLV_TAG_STRUCT_PEER_SET_RX_BLOCKSIZE_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_WAKEUP_CONFIG_CMDID,
+       WMI_TLV_TAG_STRUCT_TLV_BUF_LEN_PARAM,
+       WMI_TLV_TAG_STRUCT_SERVICE_AVAILABLE_EVENT,
+       WMI_TLV_TAG_STRUCT_PEER_ANTDIV_INFO_REQ_CMD,
+       WMI_TLV_TAG_STRUCT_PEER_ANTDIV_INFO_EVENT,
+       WMI_TLV_TAG_STRUCT_PEER_ANTDIV_INFO,
+       WMI_TLV_TAG_STRUCT_PDEV_GET_ANTDIV_STATUS_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_ANTDIV_STATUS_EVENT,
+       WMI_TLV_TAG_STRUCT_MNT_FILTER_CMD,
+       WMI_TLV_TAG_STRUCT_GET_CHIP_POWER_STATS_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_CHIP_POWER_STATS_EVENT,
+       WMI_TLV_TAG_STRUCT_COEX_GET_ANTENNA_ISOLATION_CMD,
+       WMI_TLV_TAG_STRUCT_COEX_REPORT_ISOLATION_EVENT,
+       WMI_TLV_TAG_STRUCT_CHAN_CCA_STATS,
+       WMI_TLV_TAG_STRUCT_PEER_SIGNAL_STATS,
+       WMI_TLV_TAG_STRUCT_TX_STATS,
+       WMI_TLV_TAG_STRUCT_PEER_AC_TX_STATS,
+       WMI_TLV_TAG_STRUCT_RX_STATS,
+       WMI_TLV_TAG_STRUCT_PEER_AC_RX_STATS,
+       WMI_TLV_TAG_STRUCT_REPORT_STATS_EVENT,
+       WMI_TLV_TAG_STRUCT_CHAN_CCA_STATS_THRESH,
+       WMI_TLV_TAG_STRUCT_PEER_SIGNAL_STATS_THRESH,
+       WMI_TLV_TAG_STRUCT_TX_STATS_THRESH,
+       WMI_TLV_TAG_STRUCT_RX_STATS_THRESH,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_STATS_THRESHOLD_CMD,
+       WMI_TLV_TAG_STRUCT_REQUEST_WLAN_STATS_CMD,
+       WMI_TLV_TAG_STRUCT_RX_AGGR_FAILURE_EVENT,
+       WMI_TLV_TAG_STRUCT_RX_AGGR_FAILURE_INFO,
+       WMI_TLV_TAG_STRUCT_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMD,
+       WMI_TLV_TAG_STRUCT_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENT,
+       WMI_TLV_TAG_STRUCT_PDEV_BAND_TO_MAC,
+       WMI_TLV_TAG_STRUCT_TBTT_OFFSET_INFO,
+       WMI_TLV_TAG_STRUCT_TBTT_OFFSET_EXT_EVENT,
+       WMI_TLV_TAG_STRUCT_SAR_LIMITS_CMD,
+       WMI_TLV_TAG_STRUCT_SAR_LIMIT_CMD_ROW,
+       WMI_TLV_TAG_STRUCT_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMD,
+       WMI_TLV_TAG_STRUCT_VDEV_ADFS_CH_CFG_CMD,
+       WMI_TLV_TAG_STRUCT_VDEV_ADFS_OCAC_ABORT_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_DFS_RADAR_DETECTION_EVENT,
+       WMI_TLV_TAG_STRUCT_VDEV_ADFS_OCAC_COMPLETE_EVENT,
+       WMI_TLV_TAG_STRUCT_VDEV_DFS_CAC_COMPLETE_EVENT,
+       WMI_TLV_TAG_STRUCT_VENDOR_OUI,
+       WMI_TLV_TAG_STRUCT_REQUEST_RCPI_CMD,
+       WMI_TLV_TAG_STRUCT_UPDATE_RCPI_EVENT,
+       WMI_TLV_TAG_STRUCT_REQUEST_PEER_STATS_INFO_CMD,
+       WMI_TLV_TAG_STRUCT_PEER_STATS_INFO,
+       WMI_TLV_TAG_STRUCT_PEER_STATS_INFO_EVENT,
+       WMI_TLV_TAG_STRUCT_PKGID_EVENT,
+       WMI_TLV_TAG_STRUCT_CONNECTED_NLO_RSSI_PARAMS,
+       WMI_TLV_TAG_STRUCT_SET_CURRENT_COUNTRY_CMD,
+       WMI_TLV_TAG_STRUCT_REGULATORY_RULE_STRUCT,
+       WMI_TLV_TAG_STRUCT_REG_CHAN_LIST_CC_EVENT,
+       WMI_TLV_TAG_STRUCT_11D_SCAN_START_CMD,
+       WMI_TLV_TAG_STRUCT_11D_SCAN_STOP_CMD,
+       WMI_TLV_TAG_STRUCT_11D_NEW_COUNTRY_EVENT,
+       WMI_TLV_TAG_STRUCT_REQUEST_RADIO_CHAN_STATS_CMD,
+       WMI_TLV_TAG_STRUCT_RADIO_CHAN_STATS,
+       WMI_TLV_TAG_STRUCT_RADIO_CHAN_STATS_EVENT,
+       WMI_TLV_TAG_STRUCT_ROAM_PER_CONFIG,
+       WMI_TLV_TAG_STRUCT_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMD,
+       WMI_TLV_TAG_STRUCT_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENT,
+       WMI_TLV_TAG_STRUCT_BPF_SET_VDEV_ACTIVE_MODE_CMD,
+       WMI_TLV_TAG_STRUCT_HW_DATA_FILTER_CMD,
+       WMI_TLV_TAG_STRUCT_CONNECTED_NLO_BSS_BAND_RSSI_PREF,
+       WMI_TLV_TAG_STRUCT_PEER_OPER_MODE_CHANGE_EVENT,
+       WMI_TLV_TAG_STRUCT_CHIP_POWER_SAVE_FAILURE_DETECTED,
+       WMI_TLV_TAG_STRUCT_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT,
+       WMI_TLV_TAG_STRUCT_PDEV_UPDATE_PKT_ROUTING_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_CHECK_CAL_VERSION_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_CHECK_CAL_VERSION_EVENT,
+       WMI_TLV_TAG_STRUCT_PDEV_SET_DIVERSITY_GAIN_CMD,
+       WMI_TLV_TAG_STRUCT_MAC_PHY_CHAINMASK_COMBO,
+       WMI_TLV_TAG_STRUCT_MAC_PHY_CHAINMASK_CAPABILITY,
+       WMI_TLV_TAG_STRUCT_VDEV_SET_ARP_STATS_CMD,
+       WMI_TLV_TAG_STRUCT_VDEV_GET_ARP_STATS_CMD,
+       WMI_TLV_TAG_STRUCT_VDEV_GET_ARP_STATS_EVENT,
+       WMI_TLV_TAG_STRUCT_IFACE_OFFLOAD_STATS,
+       WMI_TLV_TAG_STRUCT_REQUEST_STATS_CMD_SUB_STRUCT_PARAM,
+       WMI_TLV_TAG_STRUCT_RSSI_CTL_EXT,
+       WMI_TLV_TAG_STRUCT_SINGLE_PHYERR_EXT_RX_HDR,
+       WMI_TLV_TAG_STRUCT_COEX_BT_ACTIVITY_EVENT,
+       WMI_TLV_TAG_STRUCT_VDEV_GET_TX_POWER_CMD,
+       WMI_TLV_TAG_STRUCT_VDEV_TX_POWER_EVENT,
+       WMI_TLV_TAG_STRUCT_OFFCHAN_DATA_TX_COMPL_EVENT,
+       WMI_TLV_TAG_STRUCT_OFFCHAN_DATA_TX_SEND_CMD,
+       WMI_TLV_TAG_STRUCT_TX_SEND_PARAMS,
+       WMI_TLV_TAG_STRUCT_HE_RATE_SET,
+       WMI_TLV_TAG_STRUCT_CONGESTION_STATS,
+       WMI_TLV_TAG_STRUCT_SET_INIT_COUNTRY_CMD,
+       WMI_TLV_TAG_STRUCT_SCAN_DBS_DUTY_CYCLE,
+       WMI_TLV_TAG_STRUCT_SCAN_DBS_DUTY_CYCLE_PARAM_TLV,
+       WMI_TLV_TAG_STRUCT_PDEV_DIV_GET_RSSI_ANTID,
+       WMI_TLV_TAG_STRUCT_THERM_THROT_CONFIG_REQUEST,
+       WMI_TLV_TAG_STRUCT_THERM_THROT_LEVEL_CONFIG_INFO,
+       WMI_TLV_TAG_STRUCT_THERM_THROT_STATS_EVENT,
+       WMI_TLV_TAG_STRUCT_THERM_THROT_LEVEL_STATS_INFO,
+       WMI_TLV_TAG_STRUCT_PDEV_DIV_RSSI_ANTID_EVENT,
+       WMI_TLV_TAG_STRUCT_OEM_DMA_RING_CAPABILITIES,
+       WMI_TLV_TAG_STRUCT_OEM_DMA_RING_CFG_REQ,
+       WMI_TLV_TAG_STRUCT_OEM_DMA_RING_CFG_RSP,
+       WMI_TLV_TAG_STRUCT_OEM_INDIRECT_DATA,
+       WMI_TLV_TAG_STRUCT_OEM_DMA_BUF_RELEASE,
+       WMI_TLV_TAG_STRUCT_OEM_DMA_BUF_RELEASE_ENTRY,
+       WMI_TLV_TAG_STRUCT_PDEV_BSS_CHAN_INFO_REQUEST,
+       WMI_TLV_TAG_STRUCT_PDEV_BSS_CHAN_INFO_EVENT,
+       WMI_TLV_TAG_STRUCT_ROAM_LCA_DISALLOW_CONFIG_TLV_PARAM,
+       WMI_TLV_TAG_STRUCT_VDEV_LIMIT_OFFCHAN_CMD,
+       WMI_TLV_TAG_STRUCT_ROAM_RSSI_REJECTION_OCE_CONFIG_PARAM,
+       WMI_TLV_TAG_STRUCT_UNIT_TEST_EVENT,
+       WMI_TLV_TAG_STRUCT_ROAM_FILS_OFFLOAD_TLV_PARAM,
+       WMI_TLV_TAG_STRUCT_PDEV_UPDATE_PMK_CACHE_CMD,
+       WMI_TLV_TAG_STRUCT_PMK_CACHE,
+       WMI_TLV_TAG_STRUCT_PDEV_UPDATE_FILS_HLP_PKT_CMD,
+       WMI_TLV_TAG_STRUCT_ROAM_FILS_SYNCH_TLV_PARAM,
+       WMI_TLV_TAG_STRUCT_GTK_OFFLOAD_EXTENDED_TLV_PARAM,
+       WMI_TLV_TAG_STRUCT_ROAM_BG_SCAN_ROAMING_PARAM,
+       WMI_TLV_TAG_STRUCT_OIC_PING_OFFLOAD_PARAMS_CMD,
+       WMI_TLV_TAG_STRUCT_OIC_PING_OFFLOAD_SET_ENABLE_CMD,
+       WMI_TLV_TAG_STRUCT_OIC_PING_HANDOFF_EVENT,
+       WMI_TLV_TAG_STRUCT_DHCP_LEASE_RENEW_OFFLOAD_CMD,
+       WMI_TLV_TAG_STRUCT_DHCP_LEASE_RENEW_EVENT,
+       WMI_TLV_TAG_STRUCT_BTM_CONFIG,
+       WMI_TLV_TAG_STRUCT_DEBUG_MESG_FW_DATA_STALL_PARAM,
+       WMI_TLV_TAG_STRUCT_WLM_CONFIG_CMD,
+       WMI_TLV_TAG_STRUCT_PDEV_UPDATE_CTLTABLE_REQUEST,
+       WMI_TLV_TAG_STRUCT_PDEV_UPDATE_CTLTABLE_EVENT,
+       WMI_TLV_TAG_STRUCT_ROAM_CND_SCORING_PARAM,
+       WMI_TLV_TAG_STRUCT_PDEV_CONFIG_VENDOR_OUI_ACTION,
+       WMI_TLV_TAG_STRUCT_VENDOR_OUI_EXT,
+       WMI_TLV_TAG_STRUCT_ROAM_SYNCH_FRAME_EVENT,
+       WMI_TLV_TAG_STRUCT_FD_SEND_FROM_HOST_CMD,
+       WMI_TLV_TAG_STRUCT_ENABLE_FILS_CMD,
+       WMI_TLV_TAG_STRUCT_HOST_SWFDA_EVENT,
 
        WMI_TLV_TAG_MAX
 };
@@ -1068,16 +1338,74 @@ enum wmi_tlv_service {
        WMI_TLV_SERVICE_WLAN_STATS_REPORT,
        WMI_TLV_SERVICE_TX_MSDU_ID_NEW_PARTITION_SUPPORT,
        WMI_TLV_SERVICE_DFS_PHYERR_OFFLOAD,
+       WMI_TLV_SERVICE_RCPI_SUPPORT,
+       WMI_TLV_SERVICE_FW_MEM_DUMP_SUPPORT,
+       WMI_TLV_SERVICE_PEER_STATS_INFO,
+       WMI_TLV_SERVICE_REGULATORY_DB,
+       WMI_TLV_SERVICE_11D_OFFLOAD,
+       WMI_TLV_SERVICE_HW_DATA_FILTERING,
+       WMI_TLV_SERVICE_MULTIPLE_VDEV_RESTART,
+       WMI_TLV_SERVICE_PKT_ROUTING,
+       WMI_TLV_SERVICE_CHECK_CAL_VERSION,
+       WMI_TLV_SERVICE_OFFCHAN_TX_WMI,
+       WMI_TLV_SERVICE_8SS_TX_BFEE,
+       WMI_TLV_SERVICE_EXTENDED_NSS_SUPPORT,
+       WMI_TLV_SERVICE_ACK_TIMEOUT,
+       WMI_TLV_SERVICE_PDEV_BSS_CHANNEL_INFO_64,
+       WMI_TLV_MAX_SERVICE = 128,
+
+/* NOTE:
+ * The above service flags are delivered in the wmi_service_bitmap field
+ * of the WMI_TLV_SERVICE_READY_EVENT message.
+ * The below service flags are delivered in a WMI_TLV_SERVICE_AVAILABLE_EVENT
+ * message rather than in the WMI_TLV_SERVICE_READY_EVENT message's
+ * wmi_service_bitmap field.
+ * The WMI_TLV_SERVICE_AVAILABLE_EVENT message immediately precedes the
+ * WMI_TLV_SERVICE_READY_EVENT message.
+ */
+
+       WMI_TLV_SERVICE_CHAN_LOAD_INFO = 128,
+       WMI_TLV_SERVICE_TX_PPDU_INFO_STATS_SUPPORT,
+       WMI_TLV_SERVICE_VDEV_LIMIT_OFFCHAN_SUPPORT,
+       WMI_TLV_SERVICE_FILS_SUPPORT,
+       WMI_TLV_SERVICE_WLAN_OIC_PING_OFFLOAD,
+       WMI_TLV_SERVICE_WLAN_DHCP_RENEW,
+       WMI_TLV_SERVICE_MAWC_SUPPORT,
+       WMI_TLV_SERVICE_VDEV_LATENCY_CONFIG,
+       WMI_TLV_SERVICE_PDEV_UPDATE_CTLTABLE_SUPPORT,
+       WMI_TLV_SERVICE_PKTLOG_SUPPORT_OVER_HTT,
+       WMI_TLV_SERVICE_VDEV_MULTI_GROUP_KEY_SUPPORT,
+       WMI_TLV_SERVICE_SCAN_PHYMODE_SUPPORT,
+       WMI_TLV_SERVICE_THERM_THROT,
+       WMI_TLV_SERVICE_BCN_OFFLOAD_START_STOP_SUPPORT,
+       WMI_TLV_SERVICE_WOW_WAKEUP_BY_TIMER_PATTERN,
+       WMI_TLV_SERVICE_PEER_MAP_UNMAP_V2_SUPPORT = 143,
+       WMI_TLV_SERVICE_OFFCHAN_DATA_TID_SUPPORT = 144,
+       WMI_TLV_SERVICE_RX_PROMISC_ENABLE_SUPPORT = 145,
+       WMI_TLV_SERVICE_SUPPORT_DIRECT_DMA = 146,
+       WMI_TLV_SERVICE_AP_OBSS_DETECTION_OFFLOAD = 147,
+       WMI_TLV_SERVICE_11K_NEIGHBOUR_REPORT_SUPPORT = 148,
+       WMI_TLV_SERVICE_LISTEN_INTERVAL_OFFLOAD_SUPPORT = 149,
+       WMI_TLV_SERVICE_BSS_COLOR_OFFLOAD = 150,
+       WMI_TLV_SERVICE_RUNTIME_DPD_RECAL = 151,
+       WMI_TLV_SERVICE_STA_TWT = 152,
+       WMI_TLV_SERVICE_AP_TWT = 153,
+       WMI_TLV_SERVICE_GMAC_OFFLOAD_SUPPORT = 154,
+       WMI_TLV_SERVICE_SPOOF_MAC_SUPPORT = 155,
+
+       WMI_TLV_MAX_EXT_SERVICE = 256,
 };
 
-#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
-       ((svc_id) < (len) && \
-        __le32_to_cpu((wmi_svc_bmap)[(svc_id) / (sizeof(u32))]) & \
-        BIT((svc_id) % (sizeof(u32))))
+#define WMI_TLV_EXT_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
+       ((svc_id) < (WMI_TLV_MAX_EXT_SERVICE) && \
+        (svc_id) >= (len) && \
+       __le32_to_cpu((wmi_svc_bmap)[((svc_id) - (len)) / 32]) & \
+       BIT(((((svc_id) - (len)) % 32) & 0x1f)))
 
 #define SVCMAP(x, y, len) \
        do { \
-               if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \
+               if ((WMI_SERVICE_IS_ENABLED((in), (x), (len))) || \
+                       (WMI_TLV_EXT_SERVICE_IS_ENABLED((in), (x), (len)))) \
                        __set_bit(y, out); \
        } while (0)
 
@@ -1228,6 +1556,14 @@ wmi_tlv_svc_map(const __le32 *in, unsigned long *out, size_t len)
               WMI_SERVICE_MGMT_TX_WMI, len);
 }
 
+static inline void
+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);
+}
+
 #undef SVCMAP
 
 struct wmi_tlv {
@@ -1370,6 +1706,15 @@ struct wmi_tlv_scan_chan_list_cmd {
        __le32 num_scan_chans;
 } __packed;
 
+struct wmi_scan_prob_req_oui_cmd {
+/* OUI to be used in Probe Request frame when random MAC address is
+ * requested part of scan parameters. This is applied to both FW internal
+ * scans and host initiated scans. Host can request for random MAC address
+ * with WMI_SCAN_ADD_SPOOFED_MAC_IN_PROBE_REQ flag.
+ */
+       __le32 prob_req_oui;
+}  __packed;
+
 struct wmi_tlv_start_scan_cmd {
        struct wmi_start_scan_common common;
        __le32 burst_duration_ms;
@@ -1378,6 +1723,8 @@ struct wmi_tlv_start_scan_cmd {
        __le32 num_ssids;
        __le32 ie_len;
        __le32 num_probes;
+       struct wmi_mac_addr mac_addr;
+       struct wmi_mac_addr mac_mask;
 } __packed;
 
 struct wmi_tlv_vdev_start_cmd {
index c5e1ca5..df2e92a 100644 (file)
@@ -42,6 +42,7 @@ static struct wmi_cmd_map wmi_cmd_map = {
        .stop_scan_cmdid = WMI_STOP_SCAN_CMDID,
        .scan_chan_list_cmdid = WMI_SCAN_CHAN_LIST_CMDID,
        .scan_sch_prio_tbl_cmdid = WMI_SCAN_SCH_PRIO_TBL_CMDID,
+       .scan_prob_req_oui_cmdid = WMI_CMD_UNSUPPORTED,
        .pdev_set_regdomain_cmdid = WMI_PDEV_SET_REGDOMAIN_CMDID,
        .pdev_set_channel_cmdid = WMI_PDEV_SET_CHANNEL_CMDID,
        .pdev_set_param_cmdid = WMI_PDEV_SET_PARAM_CMDID,
@@ -207,6 +208,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = {
        .stop_scan_cmdid = WMI_10X_STOP_SCAN_CMDID,
        .scan_chan_list_cmdid = WMI_10X_SCAN_CHAN_LIST_CMDID,
        .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED,
+       .scan_prob_req_oui_cmdid = WMI_CMD_UNSUPPORTED,
        .pdev_set_regdomain_cmdid = WMI_10X_PDEV_SET_REGDOMAIN_CMDID,
        .pdev_set_channel_cmdid = WMI_10X_PDEV_SET_CHANNEL_CMDID,
        .pdev_set_param_cmdid = WMI_10X_PDEV_SET_PARAM_CMDID,
@@ -374,6 +376,7 @@ static struct wmi_cmd_map wmi_10_2_4_cmd_map = {
        .stop_scan_cmdid = WMI_10_2_STOP_SCAN_CMDID,
        .scan_chan_list_cmdid = WMI_10_2_SCAN_CHAN_LIST_CMDID,
        .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED,
+       .scan_prob_req_oui_cmdid = WMI_CMD_UNSUPPORTED,
        .pdev_set_regdomain_cmdid = WMI_10_2_PDEV_SET_REGDOMAIN_CMDID,
        .pdev_set_channel_cmdid = WMI_10_2_PDEV_SET_CHANNEL_CMDID,
        .pdev_set_param_cmdid = WMI_10_2_PDEV_SET_PARAM_CMDID,
@@ -541,6 +544,7 @@ static struct wmi_cmd_map wmi_10_4_cmd_map = {
        .stop_scan_cmdid = WMI_10_4_STOP_SCAN_CMDID,
        .scan_chan_list_cmdid = WMI_10_4_SCAN_CHAN_LIST_CMDID,
        .scan_sch_prio_tbl_cmdid = WMI_10_4_SCAN_SCH_PRIO_TBL_CMDID,
+       .scan_prob_req_oui_cmdid = WMI_CMD_UNSUPPORTED,
        .pdev_set_regdomain_cmdid = WMI_10_4_PDEV_SET_REGDOMAIN_CMDID,
        .pdev_set_channel_cmdid = WMI_10_4_PDEV_SET_CHANNEL_CMDID,
        .pdev_set_param_cmdid = WMI_10_4_PDEV_SET_PARAM_CMDID,
@@ -1338,6 +1342,7 @@ static struct wmi_cmd_map wmi_10_2_cmd_map = {
        .stop_scan_cmdid = WMI_10_2_STOP_SCAN_CMDID,
        .scan_chan_list_cmdid = WMI_10_2_SCAN_CHAN_LIST_CMDID,
        .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED,
+       .scan_prob_req_oui_cmdid = WMI_CMD_UNSUPPORTED,
        .pdev_set_regdomain_cmdid = WMI_10_2_PDEV_SET_REGDOMAIN_CMDID,
        .pdev_set_channel_cmdid = WMI_10_2_PDEV_SET_CHANNEL_CMDID,
        .pdev_set_param_cmdid = WMI_10_2_PDEV_SET_PARAM_CMDID,
@@ -4357,7 +4362,7 @@ static void ath10k_tpc_config_disp_tables(struct ath10k *ar,
                                                            rate_code[i],
                                                            type);
                        snprintf(buff, sizeof(buff), "%8d ", tpc[j]);
-                       strncat(tpc_value, buff, strlen(buff));
+                       strlcat(tpc_value, buff, sizeof(tpc_value));
                }
                tpc_stats->tpc_table[type].pream_idx[i] = pream_idx;
                tpc_stats->tpc_table[type].rate_code[i] = rate_code[i];
@@ -4694,7 +4699,7 @@ ath10k_wmi_tpc_stats_final_disp_tables(struct ath10k *ar,
                                                               rate_code[i],
                                                               type, pream_idx);
                        snprintf(buff, sizeof(buff), "%8d ", tpc[j]);
-                       strncat(tpc_value, buff, strlen(buff));
+                       strlcat(tpc_value, buff, sizeof(tpc_value));
                }
                tpc_stats->tpc_table_final[type].pream_idx[i] = pream_idx;
                tpc_stats->tpc_table_final[type].rate_code[i] = rate_code[i];
@@ -5059,7 +5064,6 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work)
                return;
        }
 
-       memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
        ath10k_wmi_map_svc(ar, arg.service_map, ar->wmi.svc_map,
                           arg.service_map_len);
 
@@ -5269,6 +5273,21 @@ int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb)
        return 0;
 }
 
+void ath10k_wmi_event_service_available(struct ath10k *ar, struct sk_buff *skb)
+{
+       int ret;
+       struct wmi_svc_avail_ev_arg arg = {};
+
+       ret = ath10k_wmi_pull_svc_avail(ar, skb, &arg);
+       if (ret) {
+               ath10k_warn(ar, "failed to parse servive available event: %d\n",
+                           ret);
+       }
+
+       ath10k_wmi_map_svc_ext(ar, arg.service_map_ext, ar->wmi.svc_map,
+                              __le32_to_cpu(arg.service_map_ext_len));
+}
+
 static int ath10k_wmi_event_temperature(struct ath10k *ar, struct sk_buff *skb)
 {
        const struct wmi_pdev_temperature_event *ev;
@@ -5465,6 +5484,9 @@ static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb)
                ath10k_wmi_event_ready(ar, skb);
                ath10k_wmi_queue_set_coverage_class_work(ar);
                break;
+       case WMI_SERVICE_AVAILABLE_EVENTID:
+               ath10k_wmi_event_service_available(ar, skb);
+               break;
        default:
                ath10k_warn(ar, "Unknown eventid: %d\n", id);
                break;
@@ -5880,6 +5902,8 @@ int ath10k_wmi_connect(struct ath10k *ar)
        struct ath10k_htc_svc_conn_req conn_req;
        struct ath10k_htc_svc_conn_resp conn_resp;
 
+       memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
+
        memset(&conn_req, 0, sizeof(conn_req));
        memset(&conn_resp, 0, sizeof(conn_resp));
 
@@ -7648,7 +7672,7 @@ ath10k_wmi_10_2_4_op_gen_pdev_get_tpc_config(struct ath10k *ar, u32 param)
        cmd->param = __cpu_to_le32(param);
 
        ath10k_dbg(ar, ATH10K_DBG_WMI,
-                  "wmi pdev get tcp config param:%d\n", param);
+                  "wmi pdev get tpc config param %d\n", param);
        return skb;
 }
 
@@ -7768,7 +7792,7 @@ ath10k_wmi_fw_pdev_tx_stats_fill(const struct ath10k_fw_stats_pdev *pdev,
        len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
                         "HW rate", pdev->data_rc);
        len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
-                        "Sched self tiggers", pdev->self_triggers);
+                        "Sched self triggers", pdev->self_triggers);
        len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
                         "Dropped due to SW retries",
                         pdev->sw_retry_failure);
index 6fbc84c..16a3924 100644 (file)
@@ -201,6 +201,8 @@ enum wmi_service {
        WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS,
        WMI_SERVICE_HOST_DFS_CHECK_SUPPORT,
        WMI_SERVICE_TPC_STATS_FINAL,
+       WMI_SERVICE_RESET_CHIP,
+       WMI_SERVICE_SPOOF_MAC_SUPPORT,
 
        /* keep last */
        WMI_SERVICE_MAX,
@@ -238,6 +240,8 @@ enum wmi_10x_service {
        WMI_10X_SERVICE_MESH,
        WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT,
        WMI_10X_SERVICE_PEER_STATS,
+       WMI_10X_SERVICE_RESET_CHIP,
+       WMI_10X_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS,
 };
 
 enum wmi_main_service {
@@ -548,6 +552,10 @@ static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out,
               WMI_SERVICE_EXT_RES_CFG_SUPPORT, len);
        SVCMAP(WMI_10X_SERVICE_PEER_STATS,
               WMI_SERVICE_PEER_STATS, len);
+       SVCMAP(WMI_10X_SERVICE_RESET_CHIP,
+              WMI_SERVICE_RESET_CHIP, len);
+       SVCMAP(WMI_10X_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS,
+              WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, len);
 }
 
 static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out,
@@ -783,6 +791,7 @@ struct wmi_cmd_map {
        u32 stop_scan_cmdid;
        u32 scan_chan_list_cmdid;
        u32 scan_sch_prio_tbl_cmdid;
+       u32 scan_prob_req_oui_cmdid;
        u32 pdev_set_regdomain_cmdid;
        u32 pdev_set_channel_cmdid;
        u32 pdev_set_param_cmdid;
@@ -1183,6 +1192,7 @@ enum wmi_cmd_id {
 enum wmi_event_id {
        WMI_SERVICE_READY_EVENTID = 0x1,
        WMI_READY_EVENTID,
+       WMI_SERVICE_AVAILABLE_EVENTID,
 
        /* Scan specific events */
        WMI_SCAN_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SCAN),
@@ -3159,6 +3169,8 @@ struct wmi_start_scan_arg {
        u16 channels[64];
        struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID];
        struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID];
+       struct wmi_mac_addr mac_addr;
+       struct wmi_mac_addr mac_mask;
 };
 
 /* scan control flags */
@@ -3182,6 +3194,12 @@ struct wmi_start_scan_arg {
  */
 #define WMI_SCAN_CONTINUE_ON_ERROR 0x80
 
+/* Use random MAC address for TA for Probe Request frame and add
+ * OUI specified by WMI_SCAN_PROB_REQ_OUI_CMDID to the Probe Request frame.
+ * if OUI is not set by WMI_SCAN_PROB_REQ_OUI_CMDID then the flag is ignored.
+ */
+#define WMI_SCAN_ADD_SPOOFED_MAC_IN_PROBE_REQ   0x1000
+
 /* WMI_SCAN_CLASS_MASK must be the same value as IEEE80211_SCAN_CLASS_MASK */
 #define WMI_SCAN_CLASS_MASK 0xFF000000
 
@@ -6632,6 +6650,11 @@ struct wmi_svc_rdy_ev_arg {
        const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS];
 };
 
+struct wmi_svc_avail_ev_arg {
+       __le32 service_map_ext_len;
+       const __le32 *service_map_ext;
+};
+
 struct wmi_rdy_ev_arg {
        __le32 sw_version;
        __le32 abi_version;
@@ -6812,6 +6835,10 @@ struct wmi_wow_ev_arg {
 #define WOW_MIN_PATTERN_SIZE   1
 #define WOW_MAX_PATTERN_SIZE   148
 #define WOW_MAX_PKT_OFFSET     128
+#define WOW_HDR_LEN    (sizeof(struct ieee80211_hdr_3addr) + \
+       sizeof(struct rfc1042_hdr))
+#define WOW_MAX_REDUCE (WOW_HDR_LEN - sizeof(struct ethhdr) - \
+       offsetof(struct ieee80211_hdr_3addr, addr1))
 
 enum wmi_tdls_state {
        WMI_TDLS_DISABLE,
@@ -7052,6 +7079,7 @@ void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb);
 void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb);
 void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb);
 int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_wmi_event_service_available(struct ath10k *ar, struct sk_buff *skb);
 int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, const void *phyerr_buf,
                                 int left_len, struct wmi_phyerr_ev_arg *arg);
 void ath10k_wmi_main_op_fw_stats_fill(struct ath10k *ar,
index c4cbccb..a6b179f 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2015-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -76,6 +77,109 @@ static int ath10k_wow_cleanup(struct ath10k *ar)
        return 0;
 }
 
+/**
+ * Convert a 802.3 format to a 802.11 format.
+ *         +------------+-----------+--------+----------------+
+ * 802.3:  |dest mac(6B)|src mac(6B)|type(2B)|     body...    |
+ *         +------------+-----------+--------+----------------+
+ *                |__         |_______    |____________  |________
+ *                   |                |                |          |
+ *         +--+------------+----+-----------+---------------+-----------+
+ * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)|  8B  |type(2B)|  body...  |
+ *         +--+------------+----+-----------+---------------+-----------+
+ */
+static void ath10k_wow_convert_8023_to_80211
+                                       (struct cfg80211_pkt_pattern *new,
+                                       const struct cfg80211_pkt_pattern *old)
+{
+       u8 hdr_8023_pattern[ETH_HLEN] = {};
+       u8 hdr_8023_bit_mask[ETH_HLEN] = {};
+       u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
+       u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
+
+       int total_len = old->pkt_offset + old->pattern_len;
+       int hdr_80211_end_offset;
+
+       struct ieee80211_hdr_3addr *new_hdr_pattern =
+               (struct ieee80211_hdr_3addr *)hdr_80211_pattern;
+       struct ieee80211_hdr_3addr *new_hdr_mask =
+               (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
+       struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
+       struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
+       int hdr_len = sizeof(*new_hdr_pattern);
+
+       struct rfc1042_hdr *new_rfc_pattern =
+               (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
+       struct rfc1042_hdr *new_rfc_mask =
+               (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
+       int rfc_len = sizeof(*new_rfc_pattern);
+
+       memcpy(hdr_8023_pattern + old->pkt_offset,
+              old->pattern, ETH_HLEN - old->pkt_offset);
+       memcpy(hdr_8023_bit_mask + old->pkt_offset,
+              old->mask, ETH_HLEN - old->pkt_offset);
+
+       /* Copy destination address */
+       memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
+       memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
+
+       /* Copy source address */
+       memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
+       memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
+
+       /* Copy logic link type */
+       memcpy(&new_rfc_pattern->snap_type,
+              &old_hdr_pattern->h_proto,
+              sizeof(old_hdr_pattern->h_proto));
+       memcpy(&new_rfc_mask->snap_type,
+              &old_hdr_mask->h_proto,
+              sizeof(old_hdr_mask->h_proto));
+
+       /* Caculate new pkt_offset */
+       if (old->pkt_offset < ETH_ALEN)
+               new->pkt_offset = old->pkt_offset +
+                       offsetof(struct ieee80211_hdr_3addr, addr1);
+       else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
+               new->pkt_offset = old->pkt_offset +
+                       offsetof(struct ieee80211_hdr_3addr, addr3) -
+                       offsetof(struct ethhdr, h_source);
+       else
+               new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
+
+       /* Caculate 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))
+               hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
+       else if (total_len > ETH_ALEN)
+               hdr_80211_end_offset = total_len - ETH_ALEN +
+                       offsetof(struct ieee80211_hdr_3addr, addr3);
+       else
+               hdr_80211_end_offset = total_len +
+                       offsetof(struct ieee80211_hdr_3addr, addr1);
+
+       new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
+
+       memcpy((u8 *)new->pattern,
+              hdr_80211_pattern + new->pkt_offset,
+              new->pattern_len);
+       memcpy((u8 *)new->mask,
+              hdr_80211_bit_mask + new->pkt_offset,
+              new->pattern_len);
+
+       if (total_len > ETH_HLEN) {
+               /* Copy frame body */
+               memcpy((u8 *)new->pattern + new->pattern_len,
+                      (void *)old->pattern + ETH_HLEN - old->pkt_offset,
+                      total_len - ETH_HLEN);
+               memcpy((u8 *)new->mask + new->pattern_len,
+                      (void *)old->mask + ETH_HLEN - old->pkt_offset,
+                      total_len - ETH_HLEN);
+
+               new->pattern_len += total_len - ETH_HLEN;
+       }
+}
+
 static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
                                      struct cfg80211_wowlan *wowlan)
 {
@@ -116,22 +220,40 @@ static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
 
        for (i = 0; i < wowlan->n_patterns; i++) {
                u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
+               u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
+               u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
+               struct cfg80211_pkt_pattern new_pattern = {};
+               struct cfg80211_pkt_pattern old_pattern = patterns[i];
                int j;
 
+               new_pattern.pattern = ath_pattern;
+               new_pattern.mask = ath_bitmask;
                if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
                        continue;
-
                /* convert bytemask to bitmask */
                for (j = 0; j < patterns[i].pattern_len; j++)
                        if (patterns[i].mask[j / 8] & BIT(j % 8))
                                bitmask[j] = 0xff;
+               old_pattern.mask = bitmask;
+               new_pattern = old_pattern;
+
+               if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
+                       if (patterns[i].pkt_offset < ETH_HLEN)
+                               ath10k_wow_convert_8023_to_80211(&new_pattern,
+                                                                &old_pattern);
+                       else
+                               new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
+               }
+
+               if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
+                       return -EINVAL;
 
                ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
                                                 pattern_id,
-                                                patterns[i].pattern,
-                                                bitmask,
-                                                patterns[i].pattern_len,
-                                                patterns[i].pkt_offset);
+                                                new_pattern.pattern,
+                                                new_pattern.mask,
+                                                new_pattern.pattern_len,
+                                                new_pattern.pkt_offset);
                if (ret) {
                        ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
                                    pattern_id,
@@ -345,6 +467,12 @@ int ath10k_wow_init(struct ath10k *ar)
                return -EINVAL;
 
        ar->wow.wowlan_support = ath10k_wowlan_support;
+
+       if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
+               ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
+               ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
+       }
+
        ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
        ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
 
index 0f965e9..4e94b22 100644 (file)
@@ -645,7 +645,7 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
                         "CRC Err", tgt_stats->rx_crc_err);
        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
-                        "Key chache miss", tgt_stats->rx_key_cache_miss);
+                        "Key cache miss", tgt_stats->rx_key_cache_miss);
        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
                         "Decrypt Err", tgt_stats->rx_decrypt_err);
        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
index db95f85..808fb30 100644 (file)
@@ -426,7 +426,7 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr,
 {
        u8 *ies = NULL, *wpa_ie = NULL, *pos;
        size_t ies_len = 0;
-       struct station_info sinfo;
+       struct station_info *sinfo;
 
        ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", mac_addr, aid);
 
@@ -482,16 +482,20 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr,
                           keymgmt, ucipher, auth, apsd_info);
 
        /* send event to application */
-       memset(&sinfo, 0, sizeof(sinfo));
+       sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+       if (!sinfo)
+               return;
 
        /* TODO: sinfo.generation */
 
-       sinfo.assoc_req_ies = ies;
-       sinfo.assoc_req_ies_len = ies_len;
+       sinfo->assoc_req_ies = ies;
+       sinfo->assoc_req_ies_len = ies_len;
 
-       cfg80211_new_sta(vif->ndev, mac_addr, &sinfo, GFP_KERNEL);
+       cfg80211_new_sta(vif->ndev, mac_addr, sinfo, GFP_KERNEL);
 
        netif_wake_queue(vif->ndev);
+
+       kfree(sinfo);
 }
 
 void disconnect_timer_handler(struct timer_list *t)
index 6fee9a4..c8844f5 100644 (file)
@@ -41,7 +41,7 @@ static const int BIN_DELTA_MAX                = 10;
 
 /* we need at least 3 deltas / 4 samples for a reliable chirp detection */
 #define NUM_DIFFS 3
-static const int FFT_NUM_SAMPLES       = (NUM_DIFFS + 1);
+#define FFT_NUM_SAMPLES                (NUM_DIFFS + 1)
 
 /* Threshold for difference of delta peaks */
 static const int MAX_DIFF              = 2;
@@ -114,7 +114,7 @@ static bool ath9k_check_chirping(struct ath_softc *sc, u8 *data,
 
                ath_dbg(common, DFS, "HT40: datalen=%d, num_fft_packets=%d\n",
                        datalen, num_fft_packets);
-               if (num_fft_packets < (FFT_NUM_SAMPLES)) {
+               if (num_fft_packets < FFT_NUM_SAMPLES) {
                        ath_dbg(common, DFS, "not enough packets for chirp\n");
                        return false;
                }
@@ -136,7 +136,7 @@ static bool ath9k_check_chirping(struct ath_softc *sc, u8 *data,
                        return false;
                ath_dbg(common, DFS, "HT20: datalen=%d, num_fft_packets=%d\n",
                        datalen, num_fft_packets);
-               if (num_fft_packets < (FFT_NUM_SAMPLES)) {
+               if (num_fft_packets < FFT_NUM_SAMPLES) {
                        ath_dbg(common, DFS, "not enough packets for chirp\n");
                        return false;
                }
index a3be8ad..b6663c8 100644 (file)
@@ -2544,7 +2544,8 @@ static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw,
 }
 
 static void ath9k_mgd_prepare_tx(struct ieee80211_hw *hw,
-                                struct ieee80211_vif *vif)
+                                struct ieee80211_vif *vif,
+                                u16 duration)
 {
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
index 2c3b899..bd2b946 100644 (file)
@@ -78,7 +78,6 @@ static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch)
                if (!cur_ctl)
                        goto out_fail;
 
-               spin_lock_init(&cur_ctl->skb_lock);
                cur_ctl->ctl_blk_order = i;
                if (i == 0) {
                        ch->head_blk_ctl = cur_ctl;
@@ -275,12 +274,14 @@ static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch)
        return 0;
 }
 
-static int wcn36xx_dxe_fill_skb(struct device *dev, struct wcn36xx_dxe_ctl *ctl)
+static int wcn36xx_dxe_fill_skb(struct device *dev,
+                               struct wcn36xx_dxe_ctl *ctl,
+                               gfp_t gfp)
 {
        struct wcn36xx_dxe_desc *dxe = ctl->desc;
        struct sk_buff *skb;
 
-       skb = alloc_skb(WCN36XX_PKT_SIZE, GFP_ATOMIC);
+       skb = alloc_skb(WCN36XX_PKT_SIZE, gfp);
        if (skb == NULL)
                return -ENOMEM;
 
@@ -307,7 +308,7 @@ static int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn,
        cur_ctl = wcn_ch->head_blk_ctl;
 
        for (i = 0; i < wcn_ch->desc_num; i++) {
-               wcn36xx_dxe_fill_skb(wcn->dev, cur_ctl);
+               wcn36xx_dxe_fill_skb(wcn->dev, cur_ctl, GFP_KERNEL);
                cur_ctl = cur_ctl->next;
        }
 
@@ -367,7 +368,7 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
        spin_lock_irqsave(&ch->lock, flags);
        ctl = ch->tail_blk_ctl;
        do {
-               if (ctl->desc->ctrl & WCN36xx_DXE_CTRL_VLD)
+               if (READ_ONCE(ctl->desc->ctrl) & WCN36xx_DXE_CTRL_VLD)
                        break;
                if (ctl->skb) {
                        dma_unmap_single(wcn->dev, ctl->desc->src_addr_l,
@@ -377,18 +378,16 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
                                /* Keep frame until TX status comes */
                                ieee80211_free_txskb(wcn->hw, ctl->skb);
                        }
-                       spin_lock(&ctl->skb_lock);
+
                        if (wcn->queues_stopped) {
                                wcn->queues_stopped = false;
                                ieee80211_wake_queues(wcn->hw);
                        }
-                       spin_unlock(&ctl->skb_lock);
 
                        ctl->skb = NULL;
                }
                ctl = ctl->next;
-       } while (ctl != ch->head_blk_ctl &&
-              !(ctl->desc->ctrl & WCN36xx_DXE_CTRL_VLD));
+       } while (ctl != ch->head_blk_ctl);
 
        ch->tail_blk_ctl = ctl;
        spin_unlock_irqrestore(&ch->lock, flags);
@@ -530,10 +529,10 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
                int_mask = WCN36XX_DXE_INT_CH3_MASK;
        }
 
-       while (!(dxe->ctrl & WCN36xx_DXE_CTRL_VLD)) {
+       while (!(READ_ONCE(dxe->ctrl) & WCN36xx_DXE_CTRL_VLD)) {
                skb = ctl->skb;
                dma_addr = dxe->dst_addr_l;
-               ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl);
+               ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl, GFP_ATOMIC);
                if (0 == ret) {
                        /* new skb allocation ok. Use the new one and queue
                         * the old one to network system.
@@ -654,8 +653,6 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
        spin_lock_irqsave(&ch->lock, flags);
        ctl = ch->head_blk_ctl;
 
-       spin_lock(&ctl->next->skb_lock);
-
        /*
         * If skb is not null that means that we reached the tail of the ring
         * hence ring is full. Stop queues to let mac80211 back off until ring
@@ -664,11 +661,9 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
        if (NULL != ctl->next->skb) {
                ieee80211_stop_queues(wcn->hw);
                wcn->queues_stopped = true;
-               spin_unlock(&ctl->next->skb_lock);
                spin_unlock_irqrestore(&ch->lock, flags);
                return -EBUSY;
        }
-       spin_unlock(&ctl->next->skb_lock);
 
        ctl->skb = NULL;
        desc = ctl->desc;
@@ -693,7 +688,6 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
 
        /* Set source address of the SKB we send */
        ctl = ctl->next;
-       ctl->skb = skb;
        desc = ctl->desc;
        if (ctl->bd_cpu_addr) {
                wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
@@ -702,10 +696,16 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
        }
 
        desc->src_addr_l = dma_map_single(wcn->dev,
-                                         ctl->skb->data,
-                                         ctl->skb->len,
+                                         skb->data,
+                                         skb->len,
                                          DMA_TO_DEVICE);
+       if (dma_mapping_error(wcn->dev, desc->src_addr_l)) {
+               dev_err(wcn->dev, "unable to DMA map src_addr_l\n");
+               ret = -ENOMEM;
+               goto unlock;
+       }
 
+       ctl->skb = skb;
        desc->dst_addr_l = ch->dxe_wq;
        desc->fr_len = ctl->skb->len;
 
index ce58096..31b81b7 100644 (file)
@@ -422,7 +422,6 @@ struct wcn36xx_dxe_ctl {
        unsigned int            desc_phy_addr;
        int                     ctl_blk_order;
        struct sk_buff          *skb;
-       spinlock_t              skb_lock;
        void                    *bd_cpu_addr;
        dma_addr_t              bd_phy_addr;
 };
index 1829635..2aed6c2 100644 (file)
 /* version string max length (including NULL) */
 #define WCN36XX_HAL_VERSION_LENGTH  64
 
+/* How many frames until we start a-mpdu TX session */
+#define WCN36XX_AMPDU_START_THRESH     20
+
+#define WCN36XX_MAX_SCAN_SSIDS         9
+#define WCN36XX_MAX_SCAN_IE_LEN                500
+
 /* message types for messages exchanged between WDI and HAL */
 enum wcn36xx_hal_host_msg_type {
        /* Init/De-Init */
@@ -1170,7 +1176,7 @@ struct wcn36xx_hal_start_scan_offload_req_msg {
 
        /* IE field */
        u16 ie_len;
-       u8 ie[0];
+       u8 ie[WCN36XX_MAX_SCAN_IE_LEN];
 } __packed;
 
 struct wcn36xx_hal_start_scan_offload_rsp_msg {
index 69d6be5..e3b91b3 100644 (file)
@@ -353,6 +353,19 @@ static void wcn36xx_stop(struct ieee80211_hw *hw)
 
        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n");
 
+       cancel_work_sync(&wcn->scan_work);
+
+       mutex_lock(&wcn->scan_lock);
+       if (wcn->scan_req) {
+               struct cfg80211_scan_info scan_info = {
+                       .aborted = true,
+               };
+
+               ieee80211_scan_completed(wcn->hw, &scan_info);
+       }
+       wcn->scan_req = NULL;
+       mutex_unlock(&wcn->scan_lock);
+
        wcn36xx_debugfs_exit(wcn);
        wcn36xx_smd_stop(wcn);
        wcn36xx_dxe_deinit(wcn);
@@ -549,6 +562,7 @@ static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                } else {
                        wcn36xx_smd_set_bsskey(wcn,
                                vif_priv->encrypt_type,
+                               vif_priv->bss_index,
                                key_conf->keyidx,
                                key_conf->keylen,
                                key);
@@ -566,10 +580,13 @@ static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                break;
        case DISABLE_KEY:
                if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) {
+                       if (vif_priv->bss_index != WCN36XX_HAL_BSS_INVALID_IDX)
+                               wcn36xx_smd_remove_bsskey(wcn,
+                                       vif_priv->encrypt_type,
+                                       vif_priv->bss_index,
+                                       key_conf->keyidx);
+
                        vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
-                       wcn36xx_smd_remove_bsskey(wcn,
-                               vif_priv->encrypt_type,
-                               key_conf->keyidx);
                } else {
                        sta_priv->is_data_encrypted = false;
                        /* do not remove key if disassociated */
@@ -670,10 +687,18 @@ static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw,
        wcn->scan_aborted = true;
        mutex_unlock(&wcn->scan_lock);
 
-       /* ieee80211_scan_completed will be called on FW scan indication */
-       wcn36xx_smd_stop_hw_scan(wcn);
-
-       cancel_work_sync(&wcn->scan_work);
+       if (get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) {
+               /* ieee80211_scan_completed will be called on FW scan
+                * indication */
+               wcn36xx_smd_stop_hw_scan(wcn);
+       } else {
+               struct cfg80211_scan_info scan_info = {
+                       .aborted = true,
+               };
+
+               cancel_work_sync(&wcn->scan_work);
+               ieee80211_scan_completed(wcn->hw, &scan_info);
+       }
 }
 
 static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta,
@@ -953,6 +978,7 @@ static int wcn36xx_add_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&wcn->conf_mutex);
 
+       vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
        list_add(&vif_priv->list, &wcn->vif_list);
        wcn36xx_smd_add_sta_self(wcn, vif);
 
index 8932af5..ea74f2b 100644 (file)
@@ -620,9 +620,13 @@ out:
 int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif,
                              struct cfg80211_scan_request *req)
 {
+       struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
        struct wcn36xx_hal_start_scan_offload_req_msg msg_body;
        int ret, i;
 
+       if (req->ie_len > WCN36XX_MAX_SCAN_IE_LEN)
+               return -EINVAL;
+
        mutex_lock(&wcn->hal_mutex);
        INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_SCAN_OFFLOAD_REQ);
 
@@ -631,6 +635,7 @@ int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif,
        msg_body.max_ch_time = 100;
        msg_body.scan_hidden = 1;
        memcpy(msg_body.mac, vif->addr, ETH_ALEN);
+       msg_body.bss_type = vif_priv->bss_type;
        msg_body.p2p_search = vif->p2p;
 
        msg_body.num_ssid = min_t(u8, req->n_ssids, ARRAY_SIZE(msg_body.ssids));
@@ -646,6 +651,14 @@ int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif,
        for (i = 0; i < msg_body.num_channel; i++)
                msg_body.channels[i] = req->channels[i]->hw_value;
 
+       msg_body.header.len -= WCN36XX_MAX_SCAN_IE_LEN;
+
+       if (req->ie_len > 0) {
+               msg_body.ie_len = req->ie_len;
+               msg_body.header.len += req->ie_len;
+               memcpy(msg_body.ie, req->ie, req->ie_len);
+       }
+
        PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
 
        wcn36xx_dbg(WCN36XX_DBG_HAL,
@@ -1399,9 +1412,10 @@ int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif,
        bss->spectrum_mgt_enable = 0;
        bss->tx_mgmt_power = 0;
        bss->max_tx_power = WCN36XX_MAX_POWER(wcn);
-
        bss->action = update;
 
+       vif_priv->bss_type = bss->bss_type;
+
        wcn36xx_dbg(WCN36XX_DBG_HAL,
                    "hal config bss bssid %pM self_mac_addr %pM bss_type %d oper_mode %d nw_type %d\n",
                    bss->bssid, bss->self_mac_addr, bss->bss_type,
@@ -1446,6 +1460,10 @@ int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif)
        int ret = 0;
 
        mutex_lock(&wcn->hal_mutex);
+
+       if (vif_priv->bss_index == WCN36XX_HAL_BSS_INVALID_IDX)
+               goto out;
+
        INIT_HAL_MSG(msg_body, WCN36XX_HAL_DELETE_BSS_REQ);
 
        msg_body.bss_index = vif_priv->bss_index;
@@ -1464,6 +1482,8 @@ int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif)
                wcn36xx_err("hal_delete_bss response failed err=%d\n", ret);
                goto out;
        }
+
+       vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
 out:
        mutex_unlock(&wcn->hal_mutex);
        return ret;
@@ -1630,6 +1650,7 @@ out:
 
 int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn,
                           enum ani_ed_type enc_type,
+                          u8 bssidx,
                           u8 keyidx,
                           u8 keylen,
                           u8 *key)
@@ -1639,7 +1660,7 @@ int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn,
 
        mutex_lock(&wcn->hal_mutex);
        INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_BSSKEY_REQ);
-       msg_body.bss_idx = 0;
+       msg_body.bss_idx = bssidx;
        msg_body.enc_type = enc_type;
        msg_body.num_keys = 1;
        msg_body.keys[0].id = keyidx;
@@ -1700,6 +1721,7 @@ out:
 
 int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn,
                              enum ani_ed_type enc_type,
+                             u8 bssidx,
                              u8 keyidx)
 {
        struct wcn36xx_hal_remove_bss_key_req_msg msg_body;
@@ -1707,7 +1729,7 @@ int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn,
 
        mutex_lock(&wcn->hal_mutex);
        INIT_HAL_MSG(msg_body, WCN36XX_HAL_RMV_BSSKEY_REQ);
-       msg_body.bss_idx = 0;
+       msg_body.bss_idx = bssidx;
        msg_body.enc_type = enc_type;
        msg_body.key_id = keyidx;
 
@@ -2132,11 +2154,13 @@ static int wcn36xx_smd_hw_scan_ind(struct wcn36xx *wcn, void *buf, size_t len)
                return -EIO;
        }
 
-       wcn36xx_dbg(WCN36XX_DBG_HAL, "scan indication (type %x)", rsp->type);
+       wcn36xx_dbg(WCN36XX_DBG_HAL, "scan indication (type %x)\n", rsp->type);
 
        switch (rsp->type) {
        case WCN36XX_HAL_SCAN_IND_FAILED:
+       case WCN36XX_HAL_SCAN_IND_DEQUEUED:
                scan_info.aborted = true;
+               /* fall through */
        case WCN36XX_HAL_SCAN_IND_COMPLETED:
                mutex_lock(&wcn->scan_lock);
                wcn->scan_req = NULL;
@@ -2147,7 +2171,6 @@ static int wcn36xx_smd_hw_scan_ind(struct wcn36xx *wcn, void *buf, size_t len)
                break;
        case WCN36XX_HAL_SCAN_IND_STARTED:
        case WCN36XX_HAL_SCAN_IND_FOREIGN_CHANNEL:
-       case WCN36XX_HAL_SCAN_IND_DEQUEUED:
        case WCN36XX_HAL_SCAN_IND_PREEMPTED:
        case WCN36XX_HAL_SCAN_IND_RESTARTED:
                break;
index 8076edf..61bb8d4 100644 (file)
@@ -97,6 +97,7 @@ int wcn36xx_smd_set_stakey(struct wcn36xx *wcn,
                           u8 sta_index);
 int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn,
                           enum ani_ed_type enc_type,
+                          u8 bssidx,
                           u8 keyidx,
                           u8 keylen,
                           u8 *key);
@@ -106,6 +107,7 @@ int wcn36xx_smd_remove_stakey(struct wcn36xx *wcn,
                              u8 sta_index);
 int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn,
                              enum ani_ed_type enc_type,
+                             u8 bssidx,
                              u8 keyidx);
 int wcn36xx_smd_enter_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif);
 int wcn36xx_smd_exit_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif);
index b1768ed..a690237 100644 (file)
@@ -273,6 +273,7 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
        bool bcast = is_broadcast_ether_addr(hdr->addr1) ||
                is_multicast_ether_addr(hdr->addr1);
        struct wcn36xx_tx_bd bd;
+       int ret;
 
        memset(&bd, 0, sizeof(bd));
 
@@ -317,5 +318,17 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
        buff_to_be((u32 *)&bd, sizeof(bd)/sizeof(u32));
        bd.tx_bd_sign = 0xbdbdbdbd;
 
-       return wcn36xx_dxe_tx_frame(wcn, vif_priv, &bd, skb, is_low);
+       ret = wcn36xx_dxe_tx_frame(wcn, vif_priv, &bd, skb, is_low);
+       if (ret && bd.tx_comp) {
+               /* If the skb has not been transmitted,
+                * don't keep a reference to it.
+                */
+               spin_lock_irqsave(&wcn->dxe_lock, flags);
+               wcn->tx_ack_skb = NULL;
+               spin_unlock_irqrestore(&wcn->dxe_lock, flags);
+
+               ieee80211_wake_queues(wcn->hw);
+       }
+
+       return ret;
 }
index 5854adf..9343989 100644 (file)
 #define WLAN_NV_FILE               "wlan/prima/WCNSS_qcom_wlan_nv.bin"
 #define WCN36XX_AGGR_BUFFER_SIZE 64
 
-/* How many frames until we start a-mpdu TX session */
-#define WCN36XX_AMPDU_START_THRESH     20
-
-#define WCN36XX_MAX_SCAN_SSIDS         9
-#define WCN36XX_MAX_SCAN_IE_LEN                500
-
 extern unsigned int wcn36xx_dbg_mask;
 
 enum wcn36xx_debug_mask {
@@ -123,6 +117,7 @@ struct wcn36xx_vif {
        bool is_joining;
        bool sta_assoc;
        struct wcn36xx_hal_mac_ssid ssid;
+       enum wcn36xx_hal_bss_type bss_type;
 
        /* Power management */
        enum wcn36xx_power_state pw_state;
index 8c90b31..11e46e4 100644 (file)
@@ -1200,8 +1200,12 @@ static const struct file_operations fops_freq = {
 static int wil_link_debugfs_show(struct seq_file *s, void *data)
 {
        struct wil6210_priv *wil = s->private;
-       struct station_info sinfo;
-       int i, rc;
+       struct station_info *sinfo;
+       int i, rc = 0;
+
+       sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+       if (!sinfo)
+               return -ENOMEM;
 
        for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
                struct wil_sta_info *p = &wil->sta[i];
@@ -1229,19 +1233,21 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data)
 
                vif = (mid < wil->max_vifs) ? wil->vifs[mid] : NULL;
                if (vif) {
-                       rc = wil_cid_fill_sinfo(vif, i, &sinfo);
+                       rc = wil_cid_fill_sinfo(vif, i, sinfo);
                        if (rc)
-                               return rc;
+                               goto out;
 
-                       seq_printf(s, "  Tx_mcs = %d\n", sinfo.txrate.mcs);
-                       seq_printf(s, "  Rx_mcs = %d\n", sinfo.rxrate.mcs);
-                       seq_printf(s, "  SQ     = %d\n", sinfo.signal);
+                       seq_printf(s, "  Tx_mcs = %d\n", sinfo->txrate.mcs);
+                       seq_printf(s, "  Rx_mcs = %d\n", sinfo->rxrate.mcs);
+                       seq_printf(s, "  SQ     = %d\n", sinfo->signal);
                } else {
                        seq_puts(s, "  INVALID MID\n");
                }
        }
 
-       return 0;
+out:
+       kfree(sinfo);
+       return rc;
 }
 
 static int wil_link_seq_open(struct inode *inode, struct file *file)
index a4b413e..82aec6b 100644 (file)
@@ -391,7 +391,7 @@ static void wil_fw_error_worker(struct work_struct *work)
        struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
                                                fw_error_worker);
        struct net_device *ndev = wil->main_ndev;
-       struct wireless_dev *wdev = ndev->ieee80211_ptr;
+       struct wireless_dev *wdev;
 
        wil_dbg_misc(wil, "fw error worker\n");
 
@@ -399,6 +399,7 @@ static void wil_fw_error_worker(struct work_struct *work)
                wil_info(wil, "No recovery - interface is down\n");
                return;
        }
+       wdev = ndev->ieee80211_ptr;
 
        /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
         * passed since last recovery attempt
index a3dda9a..90de9a9 100644 (file)
@@ -824,7 +824,7 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len)
        struct wireless_dev *wdev = vif_to_wdev(vif);
        struct wmi_connect_event *evt = d;
        int ch; /* channel number */
-       struct station_info sinfo;
+       struct station_info *sinfo;
        u8 *assoc_req_ie, *assoc_resp_ie;
        size_t assoc_req_ielen, assoc_resp_ielen;
        /* capinfo(u16) + listen_interval(u16) + IEs */
@@ -940,6 +940,7 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len)
                vif->bss = NULL;
        } else if ((wdev->iftype == NL80211_IFTYPE_AP) ||
                   (wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
+
                if (rc) {
                        if (disable_ap_sme)
                                /* notify new_sta has failed */
@@ -947,16 +948,22 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len)
                        goto out;
                }
 
-               memset(&sinfo, 0, sizeof(sinfo));
+               sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+               if (!sinfo) {
+                       rc = -ENOMEM;
+                       goto out;
+               }
 
-               sinfo.generation = wil->sinfo_gen++;
+               sinfo->generation = wil->sinfo_gen++;
 
                if (assoc_req_ie) {
-                       sinfo.assoc_req_ies = assoc_req_ie;
-                       sinfo.assoc_req_ies_len = assoc_req_ielen;
+                       sinfo->assoc_req_ies = assoc_req_ie;
+                       sinfo->assoc_req_ies_len = assoc_req_ielen;
                }
 
-               cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
+               cfg80211_new_sta(ndev, evt->bssid, sinfo, GFP_KERNEL);
+
+               kfree(sinfo);
        } else {
                wil_err(wil, "unhandled iftype %d for CID %d\n", wdev->iftype,
                        evt->cid);
index 6837064..6b0e1ec 100644 (file)
@@ -1484,7 +1484,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
        int slot, firstused;
        bool frame_succeed;
        int skip;
-       static u8 err_out1, err_out2;
+       static u8 err_out1;
 
        ring = parse_cookie(dev, status->cookie, &slot);
        if (unlikely(!ring))
@@ -1518,13 +1518,13 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
                        }
                } else {
                        /* More than a single header/data pair were missed.
-                        * Report this error once.
+                        * Report this error, and reset the controller to
+                        * revive operation.
                         */
-                       if (!err_out2)
-                               b43dbg(dev->wl,
-                                      "Out of order TX status report on DMA ring %d. Expected %d, but got %d\n",
-                                      ring->index, firstused, slot);
-                       err_out2 = 1;
+                       b43dbg(dev->wl,
+                              "Out of order TX status report on DMA ring %d. Expected %d, but got %d\n",
+                              ring->index, firstused, slot);
+                       b43_controller_restart(dev, "Out of order TX");
                        return;
                }
        }
index cfa617d..2f0c64c 100644 (file)
@@ -1064,7 +1064,7 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring,
        meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
        /* create a bounce buffer in zone_dma on mapping failure. */
        if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
-               bounce_skb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
+               bounce_skb = alloc_skb(skb->len, GFP_KERNEL | GFP_DMA);
                if (!bounce_skb) {
                        ring->current_slot = old_top_slot;
                        ring->used_slots = old_used_slots;
index 0b68240..a191541 100644 (file)
@@ -963,6 +963,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340),
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341),
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362),
+       BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43364),
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339),
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4339),
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430),
index 89b8625..f5b405c 100644 (file)
@@ -2728,9 +2728,8 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
                                   struct brcmf_bss_info_le *bi)
 {
        struct wiphy *wiphy = cfg_to_wiphy(cfg);
-       struct ieee80211_channel *notify_channel;
        struct cfg80211_bss *bss;
-       struct ieee80211_supported_band *band;
+       enum nl80211_band band;
        struct brcmu_chan ch;
        u16 channel;
        u32 freq;
@@ -2738,7 +2737,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
        u16 notify_interval;
        u8 *notify_ie;
        size_t notify_ielen;
-       s32 notify_signal;
+       struct cfg80211_inform_bss bss_data = {};
 
        if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
                brcmf_err("Bss info is larger than buffer. Discarding\n");
@@ -2753,32 +2752,33 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
        channel = bi->ctl_ch;
 
        if (channel <= CH_MAX_2G_CHANNEL)
-               band = wiphy->bands[NL80211_BAND_2GHZ];
+               band = NL80211_BAND_2GHZ;
        else
-               band = wiphy->bands[NL80211_BAND_5GHZ];
+               band = NL80211_BAND_5GHZ;
 
-       freq = ieee80211_channel_to_frequency(channel, band->band);
-       notify_channel = ieee80211_get_channel(wiphy, freq);
+       freq = ieee80211_channel_to_frequency(channel, band);
+       bss_data.chan = ieee80211_get_channel(wiphy, freq);
+       bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20;
+       bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime());
 
        notify_capability = le16_to_cpu(bi->capability);
        notify_interval = le16_to_cpu(bi->beacon_period);
        notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
        notify_ielen = le32_to_cpu(bi->ie_length);
-       notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
+       bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100;
 
        brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
        brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
        brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
        brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
-       brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
+       brcmf_dbg(CONN, "Signal: %d\n", bss_data.signal);
 
-       bss = cfg80211_inform_bss(wiphy, notify_channel,
-                                 CFG80211_BSS_FTYPE_UNKNOWN,
-                                 (const u8 *)bi->BSSID,
-                                 0, notify_capability,
-                                 notify_interval, notify_ie,
-                                 notify_ielen, notify_signal,
-                                 GFP_KERNEL);
+       bss = cfg80211_inform_bss_data(wiphy, &bss_data,
+                                      CFG80211_BSS_FTYPE_UNKNOWN,
+                                      (const u8 *)bi->BSSID,
+                                      0, notify_capability,
+                                      notify_interval, notify_ie,
+                                      notify_ielen, GFP_KERNEL);
 
        if (!bss)
                return -ENOMEM;
@@ -5498,7 +5498,7 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
        static int generation;
        u32 event = e->event_code;
        u32 reason = e->reason;
-       struct station_info sinfo;
+       struct station_info *sinfo;
 
        brcmf_dbg(CONN, "event %s (%u), reason %d\n",
                  brcmf_fweh_event_name(event), event, reason);
@@ -5511,16 +5511,22 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
 
        if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
            (reason == BRCMF_E_STATUS_SUCCESS)) {
-               memset(&sinfo, 0, sizeof(sinfo));
                if (!data) {
                        brcmf_err("No IEs present in ASSOC/REASSOC_IND");
                        return -EINVAL;
                }
-               sinfo.assoc_req_ies = data;
-               sinfo.assoc_req_ies_len = e->datalen;
+
+               sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+               if (!sinfo)
+                       return -ENOMEM;
+
+               sinfo->assoc_req_ies = data;
+               sinfo->assoc_req_ies_len = e->datalen;
                generation++;
-               sinfo.generation = generation;
-               cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
+               sinfo->generation = generation;
+               cfg80211_new_sta(ndev, e->addr, sinfo, GFP_KERNEL);
+
+               kfree(sinfo);
        } else if ((event == BRCMF_E_DISASSOC_IND) ||
                   (event == BRCMF_E_DEAUTH_IND) ||
                   (event == BRCMF_E_DEAUTH)) {
@@ -6512,6 +6518,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
 
        wiphy->flags |= WIPHY_FLAG_NETNS_OK |
                        WIPHY_FLAG_PS_ON_BY_DEFAULT |
+                       WIPHY_FLAG_HAVE_AP_SME |
                        WIPHY_FLAG_OFFCHAN_TX |
                        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
        if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
index 3b829fe..927d62b 100644 (file)
@@ -689,6 +689,7 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
        case BRCM_CC_43525_CHIP_ID:
        case BRCM_CC_4365_CHIP_ID:
        case BRCM_CC_4366_CHIP_ID:
+       case BRCM_CC_43664_CHIP_ID:
                return 0x200000;
        case CY_CC_4373_CHIP_ID:
                return 0x160000;
index 94e177d..9095b83 100644 (file)
@@ -634,7 +634,7 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
 
 struct brcmf_fw_request *
 brcmf_fw_alloc_request(u32 chip, u32 chiprev,
-                      struct brcmf_firmware_mapping mapping_table[],
+                      const struct brcmf_firmware_mapping mapping_table[],
                       u32 table_size, struct brcmf_fw_name *fwnames,
                       u32 n_fwnames)
 {
index 79a2109..2893e56 100644 (file)
@@ -80,7 +80,7 @@ struct brcmf_fw_name {
 
 struct brcmf_fw_request *
 brcmf_fw_alloc_request(u32 chip, u32 chiprev,
-                      struct brcmf_firmware_mapping mapping_table[],
+                      const struct brcmf_firmware_mapping mapping_table[],
                       u32 table_size, struct brcmf_fw_name *fwnames,
                       u32 n_fwnames);
 
index f93ba6b..692235d 100644 (file)
 #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE      40
 #define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE       32
 #define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE    24
-#define BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE         16
-#define BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE         32
+#define BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE_PRE_V7  16
+#define BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE         24
+#define BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE_PRE_V7  32
+#define BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE         40
 #define BRCMF_H2D_TXFLOWRING_ITEMSIZE                  48
 
 struct msgbuf_buf_addr {
index bcef208..4b2149b 100644 (file)
@@ -2073,6 +2073,13 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
        }
 
        pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+
+       /* firmware requires unique mac address for p2pdev interface */
+       if (addr && ether_addr_equal(addr, pri_ifp->mac_addr)) {
+               brcmf_err("discovery vif must be different from primary interface\n");
+               return ERR_PTR(-EINVAL);
+       }
+
        brcmf_p2p_generate_bss_mac(p2p, addr);
        brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
 
index 091c191..f0797ae 100644 (file)
@@ -59,7 +59,7 @@ BRCMF_FW_DEF(4366B, "brcmfmac4366b-pcie");
 BRCMF_FW_DEF(4366C, "brcmfmac4366c-pcie");
 BRCMF_FW_DEF(4371, "brcmfmac4371-pcie");
 
-static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
+static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
        BRCMF_FW_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602),
        BRCMF_FW_ENTRY(BRCM_CC_43465_CHIP_ID, 0xFFFFFFF0, 4366C),
        BRCMF_FW_ENTRY(BRCM_CC_4350_CHIP_ID, 0x000000FF, 4350C),
@@ -75,6 +75,7 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
        BRCMF_FW_ENTRY(BRCM_CC_4365_CHIP_ID, 0xFFFFFFF0, 4365C),
        BRCMF_FW_ENTRY(BRCM_CC_4366_CHIP_ID, 0x0000000F, 4366B),
        BRCMF_FW_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFF0, 4366C),
+       BRCMF_FW_ENTRY(BRCM_CC_43664_CHIP_ID, 0xFFFFFFF0, 4366C),
        BRCMF_FW_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
 };
 
@@ -104,7 +105,8 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
 #define BRCMF_PCIE_PCIE2REG_MAILBOXMASK                0x4C
 #define BRCMF_PCIE_PCIE2REG_CONFIGADDR         0x120
 #define BRCMF_PCIE_PCIE2REG_CONFIGDATA         0x124
-#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX                0x140
+#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_0      0x140
+#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_1      0x144
 
 #define BRCMF_PCIE2_INTA                       0x01
 #define BRCMF_PCIE2_INTB                       0x02
@@ -134,11 +136,13 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
                                                 BRCMF_PCIE_MB_INT_D2H3_DB0 | \
                                                 BRCMF_PCIE_MB_INT_D2H3_DB1)
 
+#define BRCMF_PCIE_SHARED_VERSION_7            7
 #define BRCMF_PCIE_MIN_SHARED_VERSION          5
-#define BRCMF_PCIE_MAX_SHARED_VERSION          6
+#define BRCMF_PCIE_MAX_SHARED_VERSION          BRCMF_PCIE_SHARED_VERSION_7
 #define BRCMF_PCIE_SHARED_VERSION_MASK         0x00FF
 #define BRCMF_PCIE_SHARED_DMA_INDEX            0x10000
 #define BRCMF_PCIE_SHARED_DMA_2B_IDX           0x100000
+#define BRCMF_PCIE_SHARED_HOSTRDY_DB1          0x10000000
 
 #define BRCMF_PCIE_FLAGS_HTOD_SPLIT            0x4000
 #define BRCMF_PCIE_FLAGS_DTOH_SPLIT            0x8000
@@ -315,6 +319,14 @@ static const u32 brcmf_ring_max_item[BRCMF_NROF_COMMON_MSGRINGS] = {
        BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM
 };
 
+static const u32 brcmf_ring_itemsize_pre_v7[BRCMF_NROF_COMMON_MSGRINGS] = {
+       BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE,
+       BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE,
+       BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE,
+       BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE_PRE_V7,
+       BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE_PRE_V7
+};
+
 static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = {
        BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE,
        BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE,
@@ -781,6 +793,12 @@ static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo)
                               BRCMF_PCIE_MB_INT_FN0_1);
 }
 
+static void brcmf_pcie_hostready(struct brcmf_pciedev_info *devinfo)
+{
+       if (devinfo->shared.flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1)
+               brcmf_pcie_write_reg32(devinfo,
+                                      BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_1, 1);
+}
 
 static irqreturn_t brcmf_pcie_quick_check_isr(int irq, void *arg)
 {
@@ -923,7 +941,7 @@ static int brcmf_pcie_ring_mb_ring_bell(void *ctx)
 
        brcmf_dbg(PCIE, "RING !\n");
        /* Any arbitrary value will do, lets use 1 */
-       brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1);
+       brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_0, 1);
 
        return 0;
 }
@@ -998,8 +1016,14 @@ brcmf_pcie_alloc_dma_and_ring(struct brcmf_pciedev_info *devinfo, u32 ring_id,
        struct brcmf_pcie_ringbuf *ring;
        u32 size;
        u32 addr;
+       const u32 *ring_itemsize_array;
+
+       if (devinfo->shared.version < BRCMF_PCIE_SHARED_VERSION_7)
+               ring_itemsize_array = brcmf_ring_itemsize_pre_v7;
+       else
+               ring_itemsize_array = brcmf_ring_itemsize;
 
-       size = brcmf_ring_max_item[ring_id] * brcmf_ring_itemsize[ring_id];
+       size = brcmf_ring_max_item[ring_id] * ring_itemsize_array[ring_id];
        dma_buf = brcmf_pcie_init_dmabuffer_for_device(devinfo, size,
                        tcm_ring_phys_addr + BRCMF_RING_MEM_BASE_ADDR_OFFSET,
                        &dma_handle);
@@ -1009,7 +1033,7 @@ brcmf_pcie_alloc_dma_and_ring(struct brcmf_pciedev_info *devinfo, u32 ring_id,
        addr = tcm_ring_phys_addr + BRCMF_RING_MAX_ITEM_OFFSET;
        brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_max_item[ring_id]);
        addr = tcm_ring_phys_addr + BRCMF_RING_LEN_ITEMS_OFFSET;
-       brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_itemsize[ring_id]);
+       brcmf_pcie_write_tcm16(devinfo, addr, ring_itemsize_array[ring_id]);
 
        ring = kzalloc(sizeof(*ring), GFP_KERNEL);
        if (!ring) {
@@ -1018,7 +1042,7 @@ brcmf_pcie_alloc_dma_and_ring(struct brcmf_pciedev_info *devinfo, u32 ring_id,
                return NULL;
        }
        brcmf_commonring_config(&ring->commonring, brcmf_ring_max_item[ring_id],
-                               brcmf_ring_itemsize[ring_id], dma_buf);
+                               ring_itemsize_array[ring_id], dma_buf);
        ring->dma_handle = dma_handle;
        ring->devinfo = devinfo;
        brcmf_commonring_register_cb(&ring->commonring,
@@ -1727,6 +1751,7 @@ static void brcmf_pcie_setup(struct device *dev, int ret,
        init_waitqueue_head(&devinfo->mbdata_resp_wait);
 
        brcmf_pcie_intr_enable(devinfo);
+       brcmf_pcie_hostready(devinfo);
        if (brcmf_attach(&devinfo->pdev->dev, devinfo->settings) == 0)
                return;
 
@@ -1949,6 +1974,7 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev)
                brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
                brcmf_bus_change_state(bus, BRCMF_BUS_UP);
                brcmf_pcie_intr_enable(devinfo);
+               brcmf_pcie_hostready(devinfo);
                return 0;
        }
 
index 1037df7..412a05b 100644 (file)
@@ -619,7 +619,7 @@ BRCMF_FW_DEF(4354, "brcmfmac4354-sdio");
 BRCMF_FW_DEF(4356, "brcmfmac4356-sdio");
 BRCMF_FW_DEF(4373, "brcmfmac4373-sdio");
 
-static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
+static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
        BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
        BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0),
        BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4),
index a0873ad..a4308c6 100644 (file)
@@ -52,7 +52,7 @@ BRCMF_FW_DEF(43242A, "brcmfmac43242a");
 BRCMF_FW_DEF(43569, "brcmfmac43569");
 BRCMF_FW_DEF(4373, "brcmfmac4373");
 
-static struct brcmf_firmware_mapping brcmf_usb_fwnames[] = {
+static const struct brcmf_firmware_mapping brcmf_usb_fwnames[] = {
        BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
        BRCMF_FW_ENTRY(BRCM_CC_43235_CHIP_ID, 0x00000008, 43236B),
        BRCMF_FW_ENTRY(BRCM_CC_43236_CHIP_ID, 0x00000008, 43236B),
index 93d4cde..9d830d2 100644 (file)
@@ -3388,13 +3388,8 @@ void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode)
        u8 phybw40;
        phybw40 = CHSPEC_IS40(pi->radio_chanspec);
 
-       if (LCNREV_LT(pi->pubpi.phy_rev, 2)) {
-               mod_phy_reg(pi, 0x4b0, (0x1 << 5), (mode) << 5);
-               mod_phy_reg(pi, 0x4b1, (0x1 << 9), 0 << 9);
-       } else {
-               mod_phy_reg(pi, 0x4b0, (0x1 << 5), (mode) << 5);
-               mod_phy_reg(pi, 0x4b1, (0x1 << 9), 0 << 9);
-       }
+       mod_phy_reg(pi, 0x4b0, (0x1 << 5), (mode) << 5);
+       mod_phy_reg(pi, 0x4b1, (0x1 << 9), 0 << 9);
 
        if (phybw40 == 0) {
                mod_phy_reg((pi), 0x410,
index 57544a3..686f7a8 100644 (file)
@@ -57,6 +57,7 @@
 #define BRCM_CC_43602_CHIP_ID          43602
 #define BRCM_CC_4365_CHIP_ID           0x4365
 #define BRCM_CC_4366_CHIP_ID           0x4366
+#define BRCM_CC_43664_CHIP_ID          43664
 #define BRCM_CC_4371_CHIP_ID           0x4371
 #define CY_CC_4373_CHIP_ID             0x4373
 
index 236b524..7c4f550 100644 (file)
@@ -3732,7 +3732,7 @@ IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"),
            IPW2100_ORD(ASSOCIATED_AP_PTR,
                                "0 if not associated, else pointer to AP table entry"),
            IPW2100_ORD(AVAILABLE_AP_CNT,
-                               "AP's decsribed in the AP table"),
+                               "AP's described in the AP table"),
            IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"),
            IPW2100_ORD(STAT_AP_ASSNS, "associations"),
            IPW2100_ORD(STAT_ASSN_FAIL, "association failures"),
index 1939478..ce3e35f 100644 (file)
@@ -1009,7 +1009,7 @@ typedef enum _ORDINAL_TABLE_1 {   // NS - means Not Supported by FW
        IPW_ORD_STAT_PERCENT_RETRIES,   // current calculation of % missed tx retries
        IPW_ORD_ASSOCIATED_AP_PTR,      // If associated, this is ptr to the associated
        // AP table entry. set to 0 if not associated
-       IPW_ORD_AVAILABLE_AP_CNT,       // # of AP's decsribed in the AP table
+       IPW_ORD_AVAILABLE_AP_CNT,       // # of AP's described in the AP table
        IPW_ORD_AP_LIST_PTR,    // Ptr to list of available APs
        IPW_ORD_STAT_AP_ASSNS,  // # of associations
        IPW_ORD_STAT_ASSN_FAIL, // # of association failures
index 87a5e41..ba3fb1d 100644 (file)
@@ -12012,7 +12012,7 @@ MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)"
 
 #ifdef CONFIG_IPW2200_QOS
 module_param(qos_enable, int, 0444);
-MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis");
+MODULE_PARM_DESC(qos_enable, "enable all QoS functionalities");
 
 module_param(qos_burst_enable, int, 0444);
 MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode");
index e6205ea..4d08d78 100644 (file)
@@ -13,7 +13,7 @@ iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.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
-iwlwifi-$(CONFIG_IWLMVM) += fw/common_rx.o fw/nvm.o
+iwlwifi-$(CONFIG_IWLMVM) += fw/common_rx.o
 iwlwifi-$(CONFIG_ACPI) += fw/acpi.o
 iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += fw/debugfs.o
 
index b2573b1..5916879 100644 (file)
@@ -1,6 +1,7 @@
 /******************************************************************************
  *
  * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * 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
@@ -27,7 +28,6 @@
 #include <linux/module.h>
 #include <linux/stringify.h>
 #include "iwl-config.h"
-#include "iwl-csr.h"
 #include "iwl-agn-hw.h"
 
 /* Highest firmware API version supported */
@@ -91,7 +91,8 @@ static const struct iwl_eeprom_params iwl1000_eeprom_params = {
        .base_params = &iwl1000_base_params,                    \
        .eeprom_params = &iwl1000_eeprom_params,                \
        .led_mode = IWL_LED_BLINK,                              \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl1000_bgn_cfg = {
        .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN",
@@ -117,7 +118,8 @@ const struct iwl_cfg iwl1000_bg_cfg = {
        .eeprom_params = &iwl1000_eeprom_params,                \
        .led_mode = IWL_LED_RF_STATE,                           \
        .rx_with_siso_diversity = true,                         \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl100_bgn_cfg = {
        .name = "Intel(R) Centrino(R) Wireless-N 100 BGN",
index 1b32ad4..a63ca88 100644 (file)
@@ -1,6 +1,7 @@
 /******************************************************************************
  *
  * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * 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
@@ -115,7 +116,8 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
        .base_params = &iwl2000_base_params,                    \
        .eeprom_params = &iwl20x0_eeprom_params,                \
        .led_mode = IWL_LED_RF_STATE,                           \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 
 const struct iwl_cfg iwl2000_2bgn_cfg = {
@@ -142,7 +144,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = {
        .base_params = &iwl2030_base_params,                    \
        .eeprom_params = &iwl20x0_eeprom_params,                \
        .led_mode = IWL_LED_RF_STATE,                           \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl2030_2bgn_cfg = {
        .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
@@ -163,7 +166,8 @@ const struct iwl_cfg iwl2030_2bgn_cfg = {
        .eeprom_params = &iwl20x0_eeprom_params,                \
        .led_mode = IWL_LED_RF_STATE,                           \
        .rx_with_siso_diversity = true,                         \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl105_bgn_cfg = {
        .name = "Intel(R) Centrino(R) Wireless-N 105 BGN",
@@ -190,7 +194,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = {
        .eeprom_params = &iwl20x0_eeprom_params,                \
        .led_mode = IWL_LED_RF_STATE,                           \
        .rx_with_siso_diversity = true,                         \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl135_bgn_cfg = {
        .name = "Intel(R) Centrino(R) Wireless-N 135 BGN",
index dffd9df..d4ba66a 100644 (file)
@@ -54,7 +54,6 @@
 #include <linux/module.h>
 #include <linux/stringify.h>
 #include "iwl-config.h"
-#include "iwl-agn-hw.h"
 
 /* Highest firmware API version supported */
 #define IWL_22000_UCODE_API_MAX        38
@@ -115,8 +114,6 @@ 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,                       \
        .device_family = IWL_DEVICE_FAMILY_22000,                       \
-       .max_inst_size = IWL60_RTC_INST_SIZE,                           \
-       .max_data_size = IWL60_RTC_DATA_SIZE,                           \
        .base_params = &iwl_22000_base_params,                          \
        .led_mode = IWL_LED_RF_STATE,                                   \
        .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_22000,          \
@@ -137,13 +134,13 @@ static const struct iwl_ht_params iwl_22000_ht_params = {
        .gen2 = true,                                                   \
        .nvm_type = IWL_NVM_EXT,                                        \
        .dbgc_supported = true,                                         \
-       .tx_cmd_queue_size = 32,                                        \
        .min_umac_error_event_table = 0x400000
 
 const struct iwl_cfg iwl22000_2ac_cfg_hr = {
        .name = "Intel(R) Dual Band Wireless AC 22000",
        .fw_name_pre = IWL_22000_HR_FW_PRE,
        IWL_DEVICE_22000,
+       .csr = &iwl_csr_v1,
        .ht_params = &iwl_22000_ht_params,
        .nvm_ver = IWL_22000_NVM_VERSION,
        .nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
@@ -154,6 +151,7 @@ const struct iwl_cfg iwl22000_2ac_cfg_hr_cdb = {
        .name = "Intel(R) Dual Band Wireless AC 22000",
        .fw_name_pre = IWL_22000_HR_CDB_FW_PRE,
        IWL_DEVICE_22000,
+       .csr = &iwl_csr_v1,
        .ht_params = &iwl_22000_ht_params,
        .nvm_ver = IWL_22000_NVM_VERSION,
        .nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
@@ -165,6 +163,7 @@ const struct iwl_cfg iwl22000_2ac_cfg_jf = {
        .name = "Intel(R) Dual Band Wireless AC 22000",
        .fw_name_pre = IWL_22000_JF_FW_PRE,
        IWL_DEVICE_22000,
+       .csr = &iwl_csr_v1,
        .ht_params = &iwl_22000_ht_params,
        .nvm_ver = IWL_22000_NVM_VERSION,
        .nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
@@ -175,6 +174,7 @@ const struct iwl_cfg iwl22000_2ax_cfg_hr = {
        .name = "Intel(R) Dual Band Wireless AX 22000",
        .fw_name_pre = IWL_22000_HR_FW_PRE,
        IWL_DEVICE_22000,
+       .csr = &iwl_csr_v1,
        .ht_params = &iwl_22000_ht_params,
        .nvm_ver = IWL_22000_NVM_VERSION,
        .nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
@@ -185,6 +185,7 @@ const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_f0 = {
        .name = "Intel(R) Dual Band Wireless AX 22000",
        .fw_name_pre = IWL_22000_HR_F0_FW_PRE,
        IWL_DEVICE_22000,
+       .csr = &iwl_csr_v1,
        .ht_params = &iwl_22000_ht_params,
        .nvm_ver = IWL_22000_NVM_VERSION,
        .nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
@@ -195,6 +196,7 @@ const struct iwl_cfg iwl22000_2ax_cfg_qnj_jf_b0 = {
        .name = "Intel(R) Dual Band Wireless AX 22000",
        .fw_name_pre = IWL_22000_JF_B0_FW_PRE,
        IWL_DEVICE_22000,
+       .csr = &iwl_csr_v1,
        .ht_params = &iwl_22000_ht_params,
        .nvm_ver = IWL_22000_NVM_VERSION,
        .nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
@@ -205,6 +207,7 @@ const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_a0 = {
        .name = "Intel(R) Dual Band Wireless AX 22000",
        .fw_name_pre = IWL_22000_HR_A0_FW_PRE,
        IWL_DEVICE_22000,
+       .csr = &iwl_csr_v1,
        .ht_params = &iwl_22000_ht_params,
        .nvm_ver = IWL_22000_NVM_VERSION,
        .nvm_calib_ver = IWL_22000_TX_POWER_VERSION,
index 4aa8f0a..a224f1b 100644 (file)
@@ -1,6 +1,7 @@
 /******************************************************************************
  *
  * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ * 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,7 +29,6 @@
 #include <linux/stringify.h>
 #include "iwl-config.h"
 #include "iwl-agn-hw.h"
-#include "iwl-csr.h"
 
 /* Highest firmware API version supported */
 #define IWL5000_UCODE_API_MAX 5
@@ -89,7 +89,8 @@ static const struct iwl_eeprom_params iwl5000_eeprom_params = {
        .base_params = &iwl5000_base_params,                    \
        .eeprom_params = &iwl5000_eeprom_params,                \
        .led_mode = IWL_LED_BLINK,                              \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl5300_agn_cfg = {
        .name = "Intel(R) Ultimate N WiFi Link 5300 AGN",
@@ -153,7 +154,8 @@ const struct iwl_cfg iwl5350_agn_cfg = {
        .eeprom_params = &iwl5000_eeprom_params,                \
        .led_mode = IWL_LED_BLINK,                              \
        .internal_wimax_coex = true,                            \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl5150_agn_cfg = {
        .name = "Intel(R) WiMAX/WiFi Link 5150 AGN",
index 39335b7..51cec0b 100644 (file)
@@ -1,6 +1,7 @@
 /******************************************************************************
  *
  * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * 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
@@ -135,7 +136,8 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = {
        .base_params = &iwl6000_g2_base_params,                 \
        .eeprom_params = &iwl6000_eeprom_params,                \
        .led_mode = IWL_LED_RF_STATE,                           \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl6005_2agn_cfg = {
        .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN",
@@ -189,7 +191,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = {
        .base_params = &iwl6000_g2_base_params,                 \
        .eeprom_params = &iwl6000_eeprom_params,                \
        .led_mode = IWL_LED_RF_STATE,                           \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl6030_2agn_cfg = {
        .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN",
@@ -225,7 +228,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = {
        .base_params = &iwl6000_g2_base_params,                 \
        .eeprom_params = &iwl6000_eeprom_params,                \
        .led_mode = IWL_LED_RF_STATE,                           \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl6035_2agn_cfg = {
        .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",
@@ -280,7 +284,8 @@ const struct iwl_cfg iwl130_bg_cfg = {
        .base_params = &iwl6000_base_params,                    \
        .eeprom_params = &iwl6000_eeprom_params,                \
        .led_mode = IWL_LED_BLINK,                              \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl6000i_2agn_cfg = {
        .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN",
@@ -313,7 +318,8 @@ const struct iwl_cfg iwl6000i_2bg_cfg = {
        .eeprom_params = &iwl6000_eeprom_params,                \
        .led_mode = IWL_LED_BLINK,                              \
        .internal_wimax_coex = true,                            \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl6050_2agn_cfg = {
        .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN",
@@ -339,7 +345,8 @@ const struct iwl_cfg iwl6050_2abg_cfg = {
        .eeprom_params = &iwl6000_eeprom_params,                \
        .led_mode = IWL_LED_BLINK,                              \
        .internal_wimax_coex = true,                            \
-       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+       .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl6150_bgn_cfg = {
        .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN",
index ce741be..69bfa82 100644 (file)
@@ -7,7 +7,8 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015        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
@@ -34,7 +35,8 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015        Intel Deutschland GmbH
+ * Copyright(c) 2018        Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -68,7 +70,6 @@
 #include <linux/module.h>
 #include <linux/stringify.h>
 #include "iwl-config.h"
-#include "iwl-agn-hw.h"
 
 /* Highest firmware API version supported */
 #define IWL7260_UCODE_API_MAX  17
@@ -160,14 +161,13 @@ static const struct iwl_ht_params iwl7000_ht_params = {
 
 #define IWL_DEVICE_7000_COMMON                                 \
        .device_family = IWL_DEVICE_FAMILY_7000,                \
-       .max_inst_size = IWL60_RTC_INST_SIZE,                   \
-       .max_data_size = IWL60_RTC_DATA_SIZE,                   \
        .base_params = &iwl7000_base_params,                    \
        .led_mode = IWL_LED_RF_STATE,                           \
        .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000,   \
        .non_shared_ant = ANT_A,                                \
        .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,    \
-       .dccm_offset = IWL7000_DCCM_OFFSET
+       .dccm_offset = IWL7000_DCCM_OFFSET,                     \
+       .csr = &iwl_csr_v1
 
 #define IWL_DEVICE_7000                                                \
        IWL_DEVICE_7000_COMMON,                                 \
index 3f4d9ba..7262e97 100644 (file)
@@ -7,7 +7,8 @@
  *
  * Copyright(c) 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2016 Intel Deutschland 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
@@ -34,6 +35,7 @@
  *
  * Copyright(c) 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2018        Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -67,7 +69,6 @@
 #include <linux/module.h>
 #include <linux/stringify.h>
 #include "iwl-config.h"
-#include "iwl-agn-hw.h"
 
 /* Highest firmware API version supported */
 #define IWL8000_UCODE_API_MAX  36
@@ -140,8 +141,6 @@ static const struct iwl_tt_params iwl8000_tt_params = {
 
 #define IWL_DEVICE_8000_COMMON                                         \
        .device_family = IWL_DEVICE_FAMILY_8000,                        \
-       .max_inst_size = IWL60_RTC_INST_SIZE,                           \
-       .max_data_size = IWL60_RTC_DATA_SIZE,                           \
        .base_params = &iwl8000_base_params,                            \
        .led_mode = IWL_LED_RF_STATE,                                   \
        .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000,           \
@@ -158,7 +157,8 @@ static const struct iwl_tt_params iwl8000_tt_params = {
        .apmg_not_supported = true,                                     \
        .nvm_type = IWL_NVM_EXT,                                        \
        .dbgc_supported = true,                                         \
-       .min_umac_error_event_table = 0x800000
+       .min_umac_error_event_table = 0x800000,                         \
+       .csr = &iwl_csr_v1
 
 #define IWL_DEVICE_8000                                                        \
        IWL_DEVICE_8000_COMMON,                                         \
index e1c869a..9706624 100644 (file)
@@ -54,7 +54,6 @@
 #include <linux/module.h>
 #include <linux/stringify.h>
 #include "iwl-config.h"
-#include "iwl-agn-hw.h"
 #include "fw/file.h"
 
 /* Highest firmware API version supported */
@@ -135,8 +134,6 @@ static const struct iwl_tt_params iwl9000_tt_params = {
        .ucode_api_max = IWL9000_UCODE_API_MAX,                         \
        .ucode_api_min = IWL9000_UCODE_API_MIN,                         \
        .device_family = IWL_DEVICE_FAMILY_9000,                        \
-       .max_inst_size = IWL60_RTC_INST_SIZE,                           \
-       .max_data_size = IWL60_RTC_DATA_SIZE,                           \
        .base_params = &iwl9000_base_params,                            \
        .led_mode = IWL_LED_RF_STATE,                                   \
        .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_9000,           \
@@ -156,7 +153,8 @@ static const struct iwl_tt_params iwl9000_tt_params = {
        .rf_id = true,                                                  \
        .nvm_type = IWL_NVM_EXT,                                        \
        .dbgc_supported = true,                                         \
-       .min_umac_error_event_table = 0x800000
+       .min_umac_error_event_table = 0x800000,                         \
+       .csr = &iwl_csr_v1
 
 const struct iwl_cfg iwl9160_2ac_cfg = {
        .name = "Intel(R) Dual Band Wireless AC 9160",
index e68254e..030482b 100644 (file)
@@ -1200,16 +1200,16 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
                return -EINVAL;
        }
 
-       if (!data->sku_cap_11n_enable && !data->sku_cap_band_24GHz_enable &&
-           !data->sku_cap_band_52GHz_enable) {
+       if (!data->sku_cap_11n_enable && !data->sku_cap_band_24ghz_enable &&
+           !data->sku_cap_band_52ghz_enable) {
                IWL_ERR(priv, "Invalid device sku\n");
                return -EINVAL;
        }
 
        IWL_DEBUG_INFO(priv,
                       "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n",
-                      data->sku_cap_band_24GHz_enable ? "" : "NOT", "enabled",
-                      data->sku_cap_band_52GHz_enable ? "" : "NOT", "enabled",
+                      data->sku_cap_band_24ghz_enable ? "" : "NOT", "enabled",
+                      data->sku_cap_band_52ghz_enable ? "" : "NOT", "enabled",
                       data->sku_cap_11n_enable ? "" : "NOT", "enabled");
 
        priv->hw_params.tx_chains_num =
index a57c722..5f6e855 100644 (file)
@@ -87,11 +87,6 @@ enum iwl_data_path_subcmd_ids {
         */
        TLC_MNG_CONFIG_CMD = 0xF,
 
-       /**
-        * @TLC_MNG_NOTIF_REQ_CMD: &struct iwl_tlc_notif_req_config_cmd
-        */
-       TLC_MNG_NOTIF_REQ_CMD = 0x10,
-
        /**
         * @TLC_MNG_UPDATE_NOTIF: &struct iwl_tlc_update_notif
         */
index 37c57bc..8d6dc91 100644 (file)
@@ -189,23 +189,37 @@ struct iwl_nvm_get_info_general {
        u8 reserved;
 } __packed; /* GRP_REGULATORY_NVM_GET_INFO_GENERAL_S_VER_1 */
 
+/**
+ * enum iwl_nvm_mac_sku_flags - flags in &iwl_nvm_get_info_sku
+ * @NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED: true if 2.4 band enabled
+ * @NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED: true if 5.2 band enabled
+ * @NVM_MAC_SKU_FLAGS_802_11N_ENABLED: true if 11n enabled
+ * @NVM_MAC_SKU_FLAGS_802_11AC_ENABLED: true if 11ac enabled
+ * @NVM_MAC_SKU_FLAGS_802_11AX_ENABLED: true if 11ax enabled
+ * @NVM_MAC_SKU_FLAGS_MIMO_DISABLED: true if MIMO disabled
+ * @NVM_MAC_SKU_FLAGS_WAPI_ENABLED: true if WAPI enabled
+ * @NVM_MAC_SKU_FLAGS_REG_CHECK_ENABLED: true if regulatory checker enabled
+ * @NVM_MAC_SKU_FLAGS_API_LOCK_ENABLED: true if API lock enabled
+ */
+enum iwl_nvm_mac_sku_flags {
+       NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED      = BIT(0),
+       NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED      = BIT(1),
+       NVM_MAC_SKU_FLAGS_802_11N_ENABLED       = BIT(2),
+       NVM_MAC_SKU_FLAGS_802_11AC_ENABLED      = BIT(3),
+       NVM_MAC_SKU_FLAGS_802_11AX_ENABLED      = BIT(4),
+       NVM_MAC_SKU_FLAGS_MIMO_DISABLED         = BIT(5),
+       NVM_MAC_SKU_FLAGS_WAPI_ENABLED          = BIT(8),
+       NVM_MAC_SKU_FLAGS_REG_CHECK_ENABLED     = BIT(14),
+       NVM_MAC_SKU_FLAGS_API_LOCK_ENABLED      = BIT(15),
+};
+
 /**
  * struct iwl_nvm_get_info_sku - mac information
- * @enable_24g: band 2.4G enabled
- * @enable_5g: band 5G enabled
- * @enable_11n: 11n enabled
- * @enable_11ac: 11ac enabled
- * @mimo_disable: MIMO enabled
- * @ext_crypto: Extended crypto enabled
+ * @mac_sku_flags: flags for SKU, see &enum iwl_nvm_mac_sku_flags
  */
 struct iwl_nvm_get_info_sku {
-       __le32 enable_24g;
-       __le32 enable_5g;
-       __le32 enable_11n;
-       __le32 enable_11ac;
-       __le32 mimo_disable;
-       __le32 ext_crypto;
-} __packed; /* GRP_REGULATORY_NVM_GET_INFO_MAC_SKU_SECTION_S_VER_1 */
+       __le32 mac_sku_flags;
+} __packed; /* REGULATORY_NVM_GET_INFO_MAC_SKU_SECTION_S_VER_2 */
 
 /**
  * struct iwl_nvm_get_info_phy - phy information
@@ -243,7 +257,7 @@ struct iwl_nvm_get_info_rsp {
        struct iwl_nvm_get_info_sku mac_sku;
        struct iwl_nvm_get_info_phy phy_sku;
        struct iwl_nvm_get_info_regulatory regulatory;
-} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_RSP_S_VER_1 */
+} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_RSP_S_VER_2 */
 
 /**
  * struct iwl_nvm_access_complete_cmd - NVM_ACCESS commands are completed
index e49a6f7..21e13a3 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * 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
@@ -28,6 +29,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * 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
 
 /**
  * enum iwl_tlc_mng_cfg_flags_enum - options for TLC config flags
- * @IWL_TLC_MNG_CFG_FLAGS_CCK_MSK: CCK support
- * @IWL_TLC_MNG_CFG_FLAGS_DD_MSK: enable DD
  * @IWL_TLC_MNG_CFG_FLAGS_STBC_MSK: enable STBC
  * @IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK: enable LDPC
- * @IWL_TLC_MNG_CFG_FLAGS_BF_MSK: enable BFER
- * @IWL_TLC_MNG_CFG_FLAGS_DCM_MSK: enable DCM
  */
 enum iwl_tlc_mng_cfg_flags {
-       IWL_TLC_MNG_CFG_FLAGS_CCK_MSK   = BIT(0),
-       IWL_TLC_MNG_CFG_FLAGS_DD_MSK    = BIT(1),
-       IWL_TLC_MNG_CFG_FLAGS_STBC_MSK  = BIT(2),
-       IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK  = BIT(3),
-       IWL_TLC_MNG_CFG_FLAGS_BF_MSK    = BIT(4),
-       IWL_TLC_MNG_CFG_FLAGS_DCM_MSK   = BIT(5),
+       IWL_TLC_MNG_CFG_FLAGS_STBC_MSK          = BIT(0),
+       IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK          = BIT(1),
 };
 
 /**
  * enum iwl_tlc_mng_cfg_cw - channel width options
- * @IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ: 20MHZ channel
- * @IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ: 40MHZ channel
- * @IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ: 80MHZ channel
- * @IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ: 160MHZ channel
- * @IWL_TLC_MNG_MAX_CH_WIDTH_LAST: maximum value
+ * @IWL_TLC_MNG_CH_WIDTH_20MHZ: 20MHZ channel
+ * @IWL_TLC_MNG_CH_WIDTH_40MHZ: 40MHZ channel
+ * @IWL_TLC_MNG_CH_WIDTH_80MHZ: 80MHZ channel
+ * @IWL_TLC_MNG_CH_WIDTH_160MHZ: 160MHZ channel
+ * @IWL_TLC_MNG_CH_WIDTH_LAST: maximum value
  */
 enum iwl_tlc_mng_cfg_cw {
-       IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ,
-       IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ,
-       IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ,
-       IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ,
-       IWL_TLC_MNG_MAX_CH_WIDTH_LAST = IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ,
+       IWL_TLC_MNG_CH_WIDTH_20MHZ,
+       IWL_TLC_MNG_CH_WIDTH_40MHZ,
+       IWL_TLC_MNG_CH_WIDTH_80MHZ,
+       IWL_TLC_MNG_CH_WIDTH_160MHZ,
+       IWL_TLC_MNG_CH_WIDTH_LAST = IWL_TLC_MNG_CH_WIDTH_160MHZ,
 };
 
 /**
  * enum iwl_tlc_mng_cfg_chains - possible chains
  * @IWL_TLC_MNG_CHAIN_A_MSK: chain A
  * @IWL_TLC_MNG_CHAIN_B_MSK: chain B
- * @IWL_TLC_MNG_CHAIN_C_MSK: chain C
  */
 enum iwl_tlc_mng_cfg_chains {
        IWL_TLC_MNG_CHAIN_A_MSK = BIT(0),
        IWL_TLC_MNG_CHAIN_B_MSK = BIT(1),
-       IWL_TLC_MNG_CHAIN_C_MSK = BIT(2),
-};
-
-/**
- * enum iwl_tlc_mng_cfg_gi - guard interval options
- * @IWL_TLC_MNG_SGI_20MHZ_MSK: enable short GI for 20MHZ
- * @IWL_TLC_MNG_SGI_40MHZ_MSK: enable short GI for 40MHZ
- * @IWL_TLC_MNG_SGI_80MHZ_MSK: enable short GI for 80MHZ
- * @IWL_TLC_MNG_SGI_160MHZ_MSK: enable short GI for 160MHZ
- */
-enum iwl_tlc_mng_cfg_gi {
-       IWL_TLC_MNG_SGI_20MHZ_MSK  = BIT(0),
-       IWL_TLC_MNG_SGI_40MHZ_MSK  = BIT(1),
-       IWL_TLC_MNG_SGI_80MHZ_MSK  = BIT(2),
-       IWL_TLC_MNG_SGI_160MHZ_MSK = BIT(3),
 };
 
 /**
@@ -145,25 +123,7 @@ enum iwl_tlc_mng_cfg_mode {
 };
 
 /**
- * enum iwl_tlc_mng_vht_he_types - VHT HE types
- * @IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU: VHT HT single user
- * @IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU_EXT: VHT HT single user extended
- * @IWL_TLC_MNG_VALID_VHT_HE_TYPES_MU: VHT HT multiple users
- * @IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED: trigger based
- * @IWL_TLC_MNG_VALID_VHT_HE_TYPES_NUM: a count of possible types
- */
-enum iwl_tlc_mng_vht_he_types {
-       IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU = 0,
-       IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU_EXT,
-       IWL_TLC_MNG_VALID_VHT_HE_TYPES_MU,
-       IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED,
-       IWL_TLC_MNG_VALID_VHT_HE_TYPES_NUM =
-               IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED,
-
-};
-
-/**
- * enum iwl_tlc_mng_ht_rates - HT/VHT rates
+ * enum iwl_tlc_mng_ht_rates - HT/VHT/HE rates
  * @IWL_TLC_MNG_HT_RATE_MCS0: index of MCS0
  * @IWL_TLC_MNG_HT_RATE_MCS1: index of MCS1
  * @IWL_TLC_MNG_HT_RATE_MCS2: index of MCS2
@@ -174,6 +134,8 @@ enum iwl_tlc_mng_vht_he_types {
  * @IWL_TLC_MNG_HT_RATE_MCS7: index of MCS7
  * @IWL_TLC_MNG_HT_RATE_MCS8: index of MCS8
  * @IWL_TLC_MNG_HT_RATE_MCS9: index of MCS9
+ * @IWL_TLC_MNG_HT_RATE_MCS10: index of MCS10
+ * @IWL_TLC_MNG_HT_RATE_MCS11: index of MCS11
  * @IWL_TLC_MNG_HT_RATE_MAX: maximal rate for HT/VHT
  */
 enum iwl_tlc_mng_ht_rates {
@@ -187,81 +149,73 @@ enum iwl_tlc_mng_ht_rates {
        IWL_TLC_MNG_HT_RATE_MCS7,
        IWL_TLC_MNG_HT_RATE_MCS8,
        IWL_TLC_MNG_HT_RATE_MCS9,
-       IWL_TLC_MNG_HT_RATE_MAX = IWL_TLC_MNG_HT_RATE_MCS9,
+       IWL_TLC_MNG_HT_RATE_MCS10,
+       IWL_TLC_MNG_HT_RATE_MCS11,
+       IWL_TLC_MNG_HT_RATE_MAX = IWL_TLC_MNG_HT_RATE_MCS11,
 };
 
 /* Maximum supported tx antennas number */
-#define MAX_RS_ANT_NUM 3
+#define MAX_NSS 2
 
 /**
  * struct tlc_config_cmd - TLC configuration
  * @sta_id: station id
  * @reserved1: reserved
- * @max_supp_ch_width: channel width
- * @flags: bitmask of &enum iwl_tlc_mng_cfg_flags
- * @chains: bitmask of &enum iwl_tlc_mng_cfg_chains
- * @max_supp_ss: valid values are 0-3, 0 - spatial streams are not supported
- * @valid_vht_he_types: bitmap of &enum iwl_tlc_mng_vht_he_types
- * @non_ht_supp_rates: bitmap of supported legacy rates
- * @ht_supp_rates: bitmap of supported HT/VHT rates, valid bits are 0-9
+ * @max_ch_width: max supported channel width from @enum iwl_tlc_mng_cfg_cw
  * @mode: &enum iwl_tlc_mng_cfg_mode
- * @reserved2: reserved
- * @he_supp_rates: bitmap of supported HE rates
+ * @chains: bitmask of &enum iwl_tlc_mng_cfg_chains
+ * @amsdu: TX amsdu is supported
+ * @flags: bitmask of &enum iwl_tlc_mng_cfg_flags
+ * @non_ht_rates: bitmap of supported legacy rates
+ * @ht_rates: bitmap of &enum iwl_tlc_mng_ht_rates, per <nss, channel-width>
+ *           pair (0 - 80mhz width and below, 1 - 160mhz).
+ * @max_mpdu_len: max MPDU length, in bytes
  * @sgi_ch_width_supp: bitmap of SGI support per channel width
- * @he_gi_support: 11ax HE guard interval
- * @max_ampdu_cnt: max AMPDU size (frames count)
+ *                    use BIT(@enum iwl_tlc_mng_cfg_cw)
+ * @reserved2: reserved
  */
 struct iwl_tlc_config_cmd {
        u8 sta_id;
        u8 reserved1[3];
-       u8 max_supp_ch_width;
+       u8 max_ch_width;
+       u8 mode;
        u8 chains;
-       u8 max_supp_ss;
-       u8 valid_vht_he_types;
+       u8 amsdu;
        __le16 flags;
-       __le16 non_ht_supp_rates;
-       __le16 ht_supp_rates[MAX_RS_ANT_NUM];
-       u8 mode;
-       u8 reserved2;
-       __le16 he_supp_rates;
+       __le16 non_ht_rates;
+       __le16 ht_rates[MAX_NSS][2];
+       __le16 max_mpdu_len;
        u8 sgi_ch_width_supp;
-       u8 he_gi_support;
-       __le32 max_ampdu_cnt;
-} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_1 */
-
-#define IWL_TLC_NOTIF_INIT_RATE_POS 0
-#define IWL_TLC_NOTIF_INIT_RATE_MSK BIT(IWL_TLC_NOTIF_INIT_RATE_POS)
-#define IWL_TLC_NOTIF_REQ_INTERVAL (500)
+       u8 reserved2[1];
+} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_2 */
 
 /**
- * struct iwl_tlc_notif_req_config_cmd - request notif on specific changes
- * @sta_id: relevant station
- * @reserved1: reserved
- * @flags: bitmap of requested notifications %IWL_TLC_NOTIF_INIT_\*
- * @interval: minimum time between notifications from TLC to the driver (msec)
- * @reserved2: reserved
+ * enum iwl_tlc_update_flags - updated fields
+ * @IWL_TLC_NOTIF_FLAG_RATE: last initial rate update
+ * @IWL_TLC_NOTIF_FLAG_AMSDU: umsdu parameters update
  */
-struct iwl_tlc_notif_req_config_cmd {
-       u8 sta_id;
-       u8 reserved1;
-       __le16 flags;
-       __le16 interval;
-       __le16 reserved2;
-} __packed; /* TLC_MNG_NOTIF_REQ_CMD_API_S_VER_1 */
+enum iwl_tlc_update_flags {
+       IWL_TLC_NOTIF_FLAG_RATE  = BIT(0),
+       IWL_TLC_NOTIF_FLAG_AMSDU = BIT(1),
+};
 
 /**
  * struct iwl_tlc_update_notif - TLC notification from FW
  * @sta_id: station id
  * @reserved: reserved
  * @flags: bitmap of notifications reported
- * @values: field per flag in struct iwl_tlc_notif_req_config_cmd
+ * @rate: current initial rate
+ * @amsdu_size: Max AMSDU size, in bytes
+ * @amsdu_enabled: bitmap for per-TID AMSDU enablement
  */
 struct iwl_tlc_update_notif {
        u8 sta_id;
-       u8 reserved;
-       __le16 flags;
-       __le32 values[16];
-} __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_1 */
+       u8 reserved[3];
+       __le32 flags;
+       __le32 rate;
+       __le32 amsdu_size;
+       __le32 amsdu_enabled;
+} __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_2 */
 
 /**
  * enum iwl_tlc_debug_flags - debug options
index dfa111b..6ac240b 100644 (file)
@@ -131,6 +131,8 @@ enum iwl_tx_queue_cfg_actions {
        TX_QUEUE_CFG_TFD_SHORT_FORMAT           = BIT(1),
 };
 
+#define IWL_DEFAULT_QUEUE_SIZE 256
+#define IWL_MGMT_QUEUE_SIZE 16
 /**
  * struct iwl_tx_queue_cfg_cmd - txq hw scheduler config command
  * @sta_id: station id
index 72259bf..507d9a4 100644 (file)
@@ -227,4 +227,40 @@ static inline void iwl_fw_cancel_dump(struct iwl_fw_runtime *fwrt)
        cancel_delayed_work_sync(&fwrt->dump.wk);
 }
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt)
+{
+       fwrt->timestamp.delay = 0;
+       cancel_delayed_work_sync(&fwrt->timestamp.wk);
+}
+
+void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay);
+
+static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt)
+{
+       cancel_delayed_work_sync(&fwrt->timestamp.wk);
+}
+
+static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt)
+{
+       if (!fwrt->timestamp.delay)
+               return;
+
+       schedule_delayed_work(&fwrt->timestamp.wk,
+                             round_jiffies_relative(fwrt->timestamp.delay));
+}
+
+#else
+
+static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt) {}
+
+static inline void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt,
+                                           u32 delay) {}
+
+static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) {}
+
+static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {}
+
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
+
 #endif  /* __iwl_fw_dbg_h__ */
index 8f005cd..8ba5a60 100644 (file)
@@ -64,6 +64,7 @@
  *****************************************************************************/
 #include "api/commands.h"
 #include "debugfs.h"
+#include "dbg.h"
 
 #define FWRT_DEBUGFS_READ_FILE_OPS(name)                               \
 static ssize_t iwl_dbgfs_##name##_read(struct iwl_fw_runtime *fwrt,    \
index d93f6a4..cbbfa8e 100644 (file)
 int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
                            struct dentry *dbgfs_dir);
 
-static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt)
-{
-       fwrt->timestamp.delay = 0;
-       cancel_delayed_work_sync(&fwrt->timestamp.wk);
-}
-
-static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt)
-{
-       cancel_delayed_work_sync(&fwrt->timestamp.wk);
-}
-
-static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt)
-{
-       if (!fwrt->timestamp.delay)
-               return;
-
-       schedule_delayed_work(&fwrt->timestamp.wk,
-                             round_jiffies_relative(fwrt->timestamp.delay));
-}
-
-void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay);
-
 #else
 static inline int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
                                          struct dentry *dbgfs_dir)
@@ -98,13 +76,4 @@ static inline int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
        return 0;
 }
 
-static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt) {}
-
-static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) {}
-
-static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {}
-
-static inline void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt,
-                                           u32 delay) {}
-
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
index 9b2805e..9d939cb 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2008 - 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
@@ -35,6 +36,7 @@
  * Copyright(c) 2005 - 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
@@ -143,6 +145,7 @@ enum iwl_ucode_tlv_type {
        IWL_UCODE_TLV_FW_DBG_TRIGGER    = 40,
        IWL_UCODE_TLV_FW_GSCAN_CAPA     = 50,
        IWL_UCODE_TLV_FW_MEM_SEG        = 51,
+       IWL_UCODE_TLV_IML               = 52,
 };
 
 struct iwl_ucode_tlv {
index b23ffe1..f491238 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 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
@@ -35,6 +36,7 @@
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 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
@@ -241,6 +243,8 @@ enum iwl_fw_type {
  * @ucode_ver: ucode version from the ucode file
  * @fw_version: firmware version string
  * @img: ucode image like ucode_rt, ucode_init, ucode_wowlan.
+ * @iml_len: length of the image loader image
+ * @iml: image loader fw image
  * @ucode_capa: capabilities parsed from the ucode file.
  * @enhance_sensitivity_table: device can do enhanced sensitivity.
  * @init_evtlog_ptr: event log offset for init ucode.
@@ -267,6 +271,8 @@ struct iwl_fw {
 
        /* ucode images */
        struct fw_img img[IWL_UCODE_TYPE_MAX];
+       size_t iml_len;
+       u8 *iml;
 
        struct iwl_ucode_capabilities ucode_capa;
        bool enhance_sensitivity_table;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/nvm.c b/drivers/net/wireless/intel/iwlwifi/fw/nvm.c
deleted file mode 100644 (file)
index bd2e1fb..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * 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) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name Intel Corporation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-#include "iwl-drv.h"
-#include "runtime.h"
-#include "fw/api/nvm-reg.h"
-#include "fw/api/commands.h"
-#include "iwl-nvm-parse.h"
-
-struct iwl_nvm_data *iwl_fw_get_nvm(struct iwl_fw_runtime *fwrt)
-{
-       struct iwl_nvm_get_info cmd = {};
-       struct iwl_nvm_get_info_rsp *rsp;
-       struct iwl_trans *trans = fwrt->trans;
-       struct iwl_nvm_data *nvm;
-       struct iwl_host_cmd hcmd = {
-               .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
-               .data = { &cmd, },
-               .len = { sizeof(cmd) },
-               .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, NVM_GET_INFO)
-       };
-       int  ret;
-       bool lar_fw_supported = !iwlwifi_mod_params.lar_disable &&
-                               fw_has_capa(&fwrt->fw->ucode_capa,
-                                           IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
-
-       ret = iwl_trans_send_cmd(trans, &hcmd);
-       if (ret)
-               return ERR_PTR(ret);
-
-       if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp),
-                "Invalid payload len in NVM response from FW %d",
-                iwl_rx_packet_payload_len(hcmd.resp_pkt))) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       rsp = (void *)hcmd.resp_pkt->data;
-       if (le32_to_cpu(rsp->general.flags) & NVM_GENERAL_FLAGS_EMPTY_OTP)
-               IWL_INFO(fwrt, "OTP is empty\n");
-
-       nvm = kzalloc(sizeof(*nvm) +
-                     sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS,
-                     GFP_KERNEL);
-       if (!nvm) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       iwl_set_hw_address_from_csr(trans, nvm);
-       /* TODO: if platform NVM has MAC address - override it here */
-
-       if (!is_valid_ether_addr(nvm->hw_addr)) {
-               IWL_ERR(fwrt, "no valid mac address was found\n");
-               ret = -EINVAL;
-               goto err_free;
-       }
-
-       IWL_INFO(trans, "base HW address: %pM\n", nvm->hw_addr);
-
-       /* Initialize general data */
-       nvm->nvm_version = le16_to_cpu(rsp->general.nvm_version);
-
-       /* Initialize MAC sku data */
-       nvm->sku_cap_11ac_enable =
-               le32_to_cpu(rsp->mac_sku.enable_11ac);
-       nvm->sku_cap_11n_enable =
-               le32_to_cpu(rsp->mac_sku.enable_11n);
-       nvm->sku_cap_band_24GHz_enable =
-               le32_to_cpu(rsp->mac_sku.enable_24g);
-       nvm->sku_cap_band_52GHz_enable =
-               le32_to_cpu(rsp->mac_sku.enable_5g);
-       nvm->sku_cap_mimo_disabled =
-               le32_to_cpu(rsp->mac_sku.mimo_disable);
-
-       /* Initialize PHY sku data */
-       nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains);
-       nvm->valid_rx_ant = (u8)le32_to_cpu(rsp->phy_sku.rx_chains);
-
-       /* Initialize regulatory data */
-       nvm->lar_enabled =
-               le32_to_cpu(rsp->regulatory.lar_enabled) && lar_fw_supported;
-
-       iwl_init_sbands(trans->dev, trans->cfg, nvm,
-                       rsp->regulatory.channel_profile,
-                       nvm->valid_tx_ant & fwrt->fw->valid_tx_ant,
-                       nvm->valid_rx_ant & fwrt->fw->valid_rx_ant,
-                       nvm->lar_enabled, false);
-
-       iwl_free_resp(&hcmd);
-       return nvm;
-
-err_free:
-       kfree(nvm);
-out:
-       iwl_free_resp(&hcmd);
-       return ERR_PTR(ret);
-}
-IWL_EXPORT_SYMBOL(iwl_fw_get_nvm);
index 1fec8e3..9b8dd7f 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
@@ -163,7 +165,7 @@ static int iwl_alloc_fw_paging_mem(struct iwl_fw_runtime *fwrt,
 static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
                               const struct fw_img *image)
 {
-       int sec_idx, idx;
+       int sec_idx, idx, ret;
        u32 offset = 0;
 
        /*
@@ -190,17 +192,23 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
         */
        if (sec_idx >= image->num_sec - 1) {
                IWL_ERR(fwrt, "Paging: Missing CSS and/or paging sections\n");
-               iwl_free_fw_paging(fwrt);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err;
        }
 
        /* copy the CSS block to the dram */
        IWL_DEBUG_FW(fwrt, "Paging: load paging CSS to FW, sec = %d\n",
                     sec_idx);
 
+       if (image->sec[sec_idx].len > fwrt->fw_paging_db[0].fw_paging_size) {
+               IWL_ERR(fwrt, "CSS block is larger than paging size\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
        memcpy(page_address(fwrt->fw_paging_db[0].fw_paging_block),
               image->sec[sec_idx].data,
-              fwrt->fw_paging_db[0].fw_paging_size);
+              image->sec[sec_idx].len);
        dma_sync_single_for_device(fwrt->trans->dev,
                                   fwrt->fw_paging_db[0].fw_paging_phys,
                                   fwrt->fw_paging_db[0].fw_paging_size,
@@ -213,17 +221,39 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
        sec_idx++;
 
        /*
-        * copy the paging blocks to the dram
-        * loop index start from 1 since that CSS block already copied to dram
-        * and CSS index is 0.
-        * loop stop at num_of_paging_blk since that last block is not full.
+        * Copy the paging blocks to the dram.  The loop index starts
+        * from 1 since the CSS block (index 0) was already copied to
+        * dram.  We use num_of_paging_blk + 1 to account for that.
         */
-       for (idx = 1; idx < fwrt->num_of_paging_blk; idx++) {
+       for (idx = 1; idx < fwrt->num_of_paging_blk + 1; idx++) {
                struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx];
+               int remaining = image->sec[sec_idx].len - offset;
+               int len = block->fw_paging_size;
+
+               /*
+                * For the last block, we copy all that is remaining,
+                * for all other blocks, we copy fw_paging_size at a
+                * time. */
+               if (idx == fwrt->num_of_paging_blk) {
+                       len = remaining;
+                       if (remaining !=
+                           fwrt->num_of_pages_in_last_blk * FW_PAGING_SIZE) {
+                               IWL_ERR(fwrt,
+                                       "Paging: last block contains more data than expected %d\n",
+                                       remaining);
+                               ret = -EINVAL;
+                               goto err;
+                       }
+               } else if (block->fw_paging_size > remaining) {
+                       IWL_ERR(fwrt,
+                               "Paging: not enough data in other in block %d (%d)\n",
+                               idx, remaining);
+                       ret = -EINVAL;
+                       goto err;
+               }
 
                memcpy(page_address(block->fw_paging_block),
-                      image->sec[sec_idx].data + offset,
-                      block->fw_paging_size);
+                      image->sec[sec_idx].data + offset, len);
                dma_sync_single_for_device(fwrt->trans->dev,
                                           block->fw_paging_phys,
                                           block->fw_paging_size,
@@ -231,30 +261,16 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt,
 
                IWL_DEBUG_FW(fwrt,
                             "Paging: copied %d paging bytes to block %d\n",
-                            fwrt->fw_paging_db[idx].fw_paging_size,
-                            idx);
-
-               offset += fwrt->fw_paging_db[idx].fw_paging_size;
-       }
-
-       /* copy the last paging block */
-       if (fwrt->num_of_pages_in_last_blk > 0) {
-               struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx];
+                            len, idx);
 
-               memcpy(page_address(block->fw_paging_block),
-                      image->sec[sec_idx].data + offset,
-                      FW_PAGING_SIZE * fwrt->num_of_pages_in_last_blk);
-               dma_sync_single_for_device(fwrt->trans->dev,
-                                          block->fw_paging_phys,
-                                          block->fw_paging_size,
-                                          DMA_BIDIRECTIONAL);
-
-               IWL_DEBUG_FW(fwrt,
-                            "Paging: copied %d pages in the last block %d\n",
-                            fwrt->num_of_pages_in_last_blk, idx);
+               offset += block->fw_paging_size;
        }
 
        return 0;
+
+err:
+       iwl_free_fw_paging(fwrt);
+       return ret;
 }
 
 static int iwl_save_fw_paging(struct iwl_fw_runtime *fwrt,
index 3fb940e..d8db1dd 100644 (file)
@@ -170,6 +170,5 @@ void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt);
 
 void iwl_fwrt_handle_notification(struct iwl_fw_runtime *fwrt,
                                  struct iwl_rx_cmd_buffer *rxb);
-struct iwl_nvm_data *iwl_fw_get_nvm(struct iwl_fw_runtime *fwrt);
 
 #endif /* __iwl_fw_runtime_h__ */
index f0f5636..c503b26 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  * 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
@@ -33,6 +34,7 @@
  *
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * 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
@@ -69,6 +71,7 @@
 #include <linux/netdevice.h>
 #include <linux/ieee80211.h>
 #include <linux/nl80211.h>
+#include "iwl-csr.h"
 
 enum iwl_device_family {
        IWL_DEVICE_FAMILY_UNDEFINED,
@@ -151,6 +154,8 @@ enum iwl_nvm_type {
 #define        ANT_AC          (ANT_A | ANT_C)
 #define ANT_BC         (ANT_B | ANT_C)
 #define ANT_ABC                (ANT_A | ANT_B | ANT_C)
+#define MAX_ANT_NUM 3
+
 
 static inline u8 num_of_ant(u8 mask)
 {
@@ -282,6 +287,52 @@ struct iwl_pwr_tx_backoff {
        u32 backoff;
 };
 
+/**
+ * struct iwl_csr_params
+ *
+ * @flag_sw_reset: reset the device
+ * @flag_mac_clock_ready:
+ *     Indicates MAC (ucode processor, etc.) is powered up and can run.
+ *     Internal resources are accessible.
+ *     NOTE:  This does not indicate that the processor is actually running.
+ *     NOTE:  This does not indicate that device has completed
+ *            init or post-power-down restore of internal SRAM memory.
+ *            Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that
+ *            SRAM is restored and uCode is in normal operation mode.
+ *            This note is relevant only for pre 5xxx devices.
+ *     NOTE:  After device reset, this bit remains "0" until host sets
+ *            INIT_DONE
+ * @flag_init_done: Host sets this to put device into fully operational
+ *     D0 power mode. Host resets this after SW_RESET to put device into
+ *     low power mode.
+ * @flag_mac_access_req: Host sets this to request and maintain MAC wakeup,
+ *     to allow host access to device-internal resources. Host must wait for
+ *     mac_clock_ready (and !GOING_TO_SLEEP) before accessing non-CSR device
+ *     registers.
+ * @flag_val_mac_access_en: mac access is enabled
+ * @flag_master_dis: disable master
+ * @flag_stop_master: stop master
+ * @addr_sw_reset: address for resetting the device
+ * @mac_addr0_otp: first part of MAC address from OTP
+ * @mac_addr1_otp: second part of MAC address from OTP
+ * @mac_addr0_strap: first part of MAC address from strap
+ * @mac_addr1_strap: second part of MAC address from strap
+ */
+struct iwl_csr_params {
+       u8 flag_sw_reset;
+       u8 flag_mac_clock_ready;
+       u8 flag_init_done;
+       u8 flag_mac_access_req;
+       u8 flag_val_mac_access_en;
+       u8 flag_master_dis;
+       u8 flag_stop_master;
+       u8 addr_sw_reset;
+       u32 mac_addr0_otp;
+       u32 mac_addr1_otp;
+       u32 mac_addr0_strap;
+       u32 mac_addr1_strap;
+};
+
 /**
  * struct iwl_cfg
  * @name: Official name of the device
@@ -294,8 +345,8 @@ struct iwl_pwr_tx_backoff {
  *     next step. Supported only in integrated solutions.
  * @ucode_api_max: Highest version of uCode API supported by driver.
  * @ucode_api_min: Lowest version of uCode API supported by driver.
- * @max_inst_size: The maximal length of the fw inst section
- * @max_data_size: The maximal length of the fw data section
+ * @max_inst_size: The maximal length of the fw inst section (only DVM)
+ * @max_data_size: The maximal length of the fw data section (only DVM)
  * @valid_tx_ant: valid transmit antenna
  * @valid_rx_ant: valid receive antenna
  * @non_shared_ant: the antenna that is for WiFi only
@@ -314,6 +365,7 @@ struct iwl_pwr_tx_backoff {
  * @mac_addr_from_csr: read HW address from CSR registers
  * @features: hw features, any combination of feature_whitelist
  * @pwr_tx_backoffs: translation table between power limits and backoffs
+ * @csr: csr flags and addresses that are different across devices
  * @max_rx_agg_size: max RX aggregation size of the ADDBA request/response
  * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response
  * @max_ht_ampdu_factor: the exponent of the max length of A-MPDU that the
@@ -333,8 +385,6 @@ struct iwl_pwr_tx_backoff {
  * @gen2: 22000 and on transport operation
  * @cdb: CDB support
  * @nvm_type: see &enum iwl_nvm_type
- * @tx_cmd_queue_size: size of the cmd queue. If zero, use the same value as
- *     the regular queues
  *
  * We enable the driver to be backward compatible wrt. hardware features.
  * API differences in uCode shouldn't be handled here but through TLVs
@@ -354,6 +404,7 @@ struct iwl_cfg {
        const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
        const char *default_nvm_file_C_step;
        const struct iwl_tt_params *thermal_params;
+       const struct iwl_csr_params *csr;
        enum iwl_device_family device_family;
        enum iwl_led_mode led_mode;
        enum iwl_nvm_type nvm_type;
@@ -369,7 +420,7 @@ struct iwl_cfg {
        u32 soc_latency;
        u16 nvm_ver;
        u16 nvm_calib_ver;
-       u16 rx_with_siso_diversity:1,
+       u32 rx_with_siso_diversity:1,
            bt_shared_single_ant:1,
            internal_wimax_coex:1,
            host_interrupt_operation_mode:1,
@@ -386,7 +437,6 @@ struct iwl_cfg {
            gen2:1,
            cdb:1,
            dbgc_supported:1;
-       u16 tx_cmd_queue_size;
        u8 valid_tx_ant;
        u8 valid_rx_ant;
        u8 non_shared_ant;
@@ -401,6 +451,36 @@ struct iwl_cfg {
        u32 extra_phy_cfg_flags;
 };
 
+static const struct iwl_csr_params iwl_csr_v1 = {
+       .flag_mac_clock_ready = 0,
+       .flag_val_mac_access_en = 0,
+       .flag_init_done = 2,
+       .flag_mac_access_req = 3,
+       .flag_sw_reset = 7,
+       .flag_master_dis = 8,
+       .flag_stop_master = 9,
+       .addr_sw_reset = (CSR_BASE + 0x020),
+       .mac_addr0_otp = 0x380,
+       .mac_addr1_otp = 0x384,
+       .mac_addr0_strap = 0x388,
+       .mac_addr1_strap = 0x38C
+};
+
+static const struct iwl_csr_params iwl_csr_v2 = {
+       .flag_init_done = 6,
+       .flag_mac_clock_ready = 20,
+       .flag_val_mac_access_en = 20,
+       .flag_mac_access_req = 21,
+       .flag_master_dis = 28,
+       .flag_stop_master = 29,
+       .flag_sw_reset = 31,
+       .addr_sw_reset = (CSR_BASE + 0x024),
+       .mac_addr0_otp = 0x30,
+       .mac_addr1_otp = 0x34,
+       .mac_addr0_strap = 0x38,
+       .mac_addr1_strap = 0x3C
+};
+
 /*
  * This list declares the config structures for all devices.
  */
index 4f0d070..ba971d3 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
@@ -34,6 +35,7 @@
  *
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 /* RESET */
 #define CSR_RESET_REG_FLAG_NEVO_RESET                (0x00000001)
 #define CSR_RESET_REG_FLAG_FORCE_NMI                 (0x00000002)
-#define CSR_RESET_REG_FLAG_SW_RESET                  (0x00000080)
 #define CSR_RESET_REG_FLAG_MASTER_DISABLED           (0x00000100)
 #define CSR_RESET_REG_FLAG_STOP_MASTER               (0x00000200)
 #define CSR_RESET_LINK_PWR_MGMT_DISABLED             (0x80000000)
  *     4:  GOING_TO_SLEEP
  *         Indicates MAC is entering a power-saving sleep power-down.
  *         Not a good time to access device-internal resources.
- *     3:  MAC_ACCESS_REQ
- *         Host sets this to request and maintain MAC wakeup, to allow host
- *         access to device-internal resources.  Host must wait for
- *         MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR
- *         device registers.
- *     2:  INIT_DONE
- *         Host sets this to put device into fully operational D0 power mode.
- *         Host resets this after SW_RESET to put device into low power mode.
- *     0:  MAC_CLOCK_READY
- *         Indicates MAC (ucode processor, etc.) is powered up and can run.
- *         Internal resources are accessible.
- *         NOTE:  This does not indicate that the processor is actually running.
- *         NOTE:  This does not indicate that device has completed
- *                init or post-power-down restore of internal SRAM memory.
- *                Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that
- *                SRAM is restored and uCode is in normal operation mode.
- *                Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and
- *                do not need to save/restore it.
- *         NOTE:  After device reset, this bit remains "0" until host sets
- *                INIT_DONE
  */
-#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY        (0x00000001)
-#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE              (0x00000004)
-#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ         (0x00000008)
 #define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP         (0x00000010)
 #define CSR_GP_CNTRL_REG_FLAG_XTAL_ON               (0x00000400)
 
-#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN           (0x00000001)
-
 #define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE         (0x07000000)
 #define CSR_GP_CNTRL_REG_FLAG_RFKILL_WAKE_L1A_EN     (0x04000000)
 #define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW          (0x08000000)
index aa2d5c1..c59ce4f 100644 (file)
@@ -179,6 +179,7 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv)
        for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++)
                kfree(drv->fw.dbg_trigger_tlv[i]);
        kfree(drv->fw.dbg_mem_tlv);
+       kfree(drv->fw.iml);
 
        for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
                iwl_free_fw_img(drv, drv->fw.img + i);
@@ -1126,6 +1127,13 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                        pieces->n_dbg_mem_tlv++;
                        break;
                        }
+               case IWL_UCODE_TLV_IML: {
+                       drv->fw.iml_len = tlv_len;
+                       drv->fw.iml = kmemdup(tlv_data, tlv_len, GFP_KERNEL);
+                       if (!drv->fw.iml)
+                               return -ENOMEM;
+                       break;
+                       }
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
@@ -1842,3 +1850,9 @@ MODULE_PARM_DESC(d0i3_timeout, "Timeout to D0i3 entry when idle (ms)");
 
 module_param_named(disable_11ac, iwlwifi_mod_params.disable_11ac, bool, 0444);
 MODULE_PARM_DESC(disable_11ac, "Disable VHT capabilities (default: false)");
+
+module_param_named(remove_when_gone,
+                  iwlwifi_mod_params.remove_when_gone, bool,
+                  0444);
+MODULE_PARM_DESC(remove_when_gone,
+                "Remove dev from PCIe bus if it is deemed inaccessible (default: false)");
index 3199d34..777f5df 100644 (file)
@@ -899,8 +899,8 @@ iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg,
                                 EEPROM_SKU_CAP);
        data->sku_cap_11n_enable = sku & EEPROM_SKU_CAP_11N_ENABLE;
        data->sku_cap_amt_enable = sku & EEPROM_SKU_CAP_AMT_ENABLE;
-       data->sku_cap_band_24GHz_enable = sku & EEPROM_SKU_CAP_BAND_24GHZ;
-       data->sku_cap_band_52GHz_enable = sku & EEPROM_SKU_CAP_BAND_52GHZ;
+       data->sku_cap_band_24ghz_enable = sku & EEPROM_SKU_CAP_BAND_24GHZ;
+       data->sku_cap_band_52ghz_enable = sku & EEPROM_SKU_CAP_BAND_52GHZ;
        data->sku_cap_ipan_enable = sku & EEPROM_SKU_CAP_IPAN_ENABLE;
        if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
                data->sku_cap_11n_enable = false;
index b338889..8be50ed 100644 (file)
@@ -81,10 +81,11 @@ struct iwl_nvm_data {
        __le16 kelvin_voltage;
        __le16 xtal_calib[2];
 
-       bool sku_cap_band_24GHz_enable;
-       bool sku_cap_band_52GHz_enable;
+       bool sku_cap_band_24ghz_enable;
+       bool sku_cap_band_52ghz_enable;
        bool sku_cap_11n_enable;
        bool sku_cap_11ac_enable;
+       bool sku_cap_11ax_enable;
        bool sku_cap_amt_enable;
        bool sku_cap_ipan_enable;
        bool sku_cap_mimo_disabled;
index f2cea1c..ac965c3 100644 (file)
@@ -6,6 +6,7 @@
  * GPL LICENSE SUMMARY
  *
  * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * 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
@@ -31,6 +32,7 @@
  * BSD LICENSE
  *
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -199,12 +201,12 @@ static int iwl_init_otp_access(struct iwl_trans *trans)
        /* Enable 40MHz radio clock */
        iwl_write32(trans, CSR_GP_CNTRL,
                    iwl_read32(trans, CSR_GP_CNTRL) |
-                   CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+                   BIT(trans->cfg->csr->flag_init_done));
 
        /* wait for clock to be ready */
        ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
-                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+                          BIT(trans->cfg->csr->flag_mac_clock_ready),
+                          BIT(trans->cfg->csr->flag_mac_clock_ready),
                           25000);
        if (ret < 0) {
                IWL_ERR(trans, "Time out access OTP\n");
index a41c46e..a7dd8a8 100644 (file)
@@ -122,6 +122,7 @@ enum iwl_uapsd_disable {
  * @lar_disable: disable LAR (regulatory), default = 0
  * @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.
  */
 struct iwl_mod_params {
        int swcrypto;
@@ -143,6 +144,7 @@ struct iwl_mod_params {
        bool lar_disable;
        bool fw_monitor;
        bool disable_11ac;
+       bool remove_when_gone;
 };
 
 #endif /* #__iwl_modparams_h__ */
index ca01746..0f9d564 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2008 - 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
@@ -35,6 +36,7 @@
  * Copyright(c) 2005 - 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
@@ -68,6 +70,7 @@
 #include <linux/export.h>
 #include <linux/etherdevice.h>
 #include <linux/pci.h>
+#include <linux/firmware.h>
 
 #include "iwl-drv.h"
 #include "iwl-modparams.h"
@@ -77,6 +80,9 @@
 #include "iwl-csr.h"
 #include "fw/acpi.h"
 #include "fw/api/nvm-reg.h"
+#include "fw/api/commands.h"
+#include "fw/api/cmdhdr.h"
+#include "fw/img.h"
 
 /* NVM offsets (in words) definitions */
 enum nvm_offsets {
@@ -292,7 +298,7 @@ static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz,
 static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                                struct iwl_nvm_data *data,
                                const __le16 * const nvm_ch_flags,
-                               bool lar_supported, bool no_wide_in_5ghz)
+                               u32 sbands_flags)
 {
        int ch_idx;
        int n_channels = 0;
@@ -316,11 +322,12 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
 
                ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
 
-               if (is_5ghz && !data->sku_cap_band_52GHz_enable)
+               if (is_5ghz && !data->sku_cap_band_52ghz_enable)
                        continue;
 
                /* workaround to disable wide channels in 5GHz */
-               if (no_wide_in_5ghz && is_5ghz) {
+               if ((sbands_flags & IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ) &&
+                   is_5ghz) {
                        ch_flags &= ~(NVM_CHANNEL_40MHZ |
                                     NVM_CHANNEL_80MHZ |
                                     NVM_CHANNEL_160MHZ);
@@ -329,7 +336,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                if (ch_flags & NVM_CHANNEL_160MHZ)
                        data->vht160_supported = true;
 
-               if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) {
+               if (!(sbands_flags & IWL_NVM_SBANDS_FLAGS_LAR) &&
+                   !(ch_flags & NVM_CHANNEL_VALID)) {
                        /*
                         * Channels might become valid later if lar is
                         * supported, hence we still want to add them to
@@ -359,7 +367,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                channel->max_power = IWL_DEFAULT_MAX_TX_POWER;
 
                /* don't put limitations in case we're using LAR */
-               if (!lar_supported)
+               if (!(sbands_flags & IWL_NVM_SBANDS_FLAGS_LAR))
                        channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx],
                                                               ch_idx, is_5ghz,
                                                               ch_flags, cfg);
@@ -455,17 +463,17 @@ 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;
 }
 
-void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
-                    struct iwl_nvm_data *data, const __le16 *nvm_ch_flags,
-                    u8 tx_chains, u8 rx_chains, bool lar_supported,
-                    bool no_wide_in_5ghz)
+static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
+                           struct iwl_nvm_data *data,
+                           const __le16 *nvm_ch_flags, u8 tx_chains,
+                           u8 rx_chains, u32 sbands_flags)
 {
        int n_channels;
        int n_used = 0;
        struct ieee80211_supported_band *sband;
 
        n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags,
-                                         lar_supported, no_wide_in_5ghz);
+                                         sbands_flags);
        sband = &data->bands[NL80211_BAND_2GHZ];
        sband->band = NL80211_BAND_2GHZ;
        sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
@@ -491,7 +499,6 @@ void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
                IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
                            n_used, n_channels);
 }
-IWL_EXPORT_SYMBOL(iwl_init_sbands);
 
 static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
                       const __le16 *phy_sku)
@@ -569,11 +576,15 @@ static void iwl_flip_hw_address(__le32 mac_addr0, __le32 mac_addr1, u8 *dest)
        dest[5] = hw_addr[0];
 }
 
-void iwl_set_hw_address_from_csr(struct iwl_trans *trans,
-                                struct iwl_nvm_data *data)
+static void iwl_set_hw_address_from_csr(struct iwl_trans *trans,
+                                       struct iwl_nvm_data *data)
 {
-       __le32 mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_STRAP));
-       __le32 mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_STRAP));
+       __le32 mac_addr0 =
+               cpu_to_le32(iwl_read32(trans,
+                                      trans->cfg->csr->mac_addr0_strap));
+       __le32 mac_addr1 =
+               cpu_to_le32(iwl_read32(trans,
+                                      trans->cfg->csr->mac_addr1_strap));
 
        iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
        /*
@@ -583,12 +594,13 @@ void iwl_set_hw_address_from_csr(struct iwl_trans *trans,
        if (is_valid_ether_addr(data->hw_addr))
                return;
 
-       mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_OTP));
-       mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_OTP));
+       mac_addr0 = cpu_to_le32(iwl_read32(trans,
+                                          trans->cfg->csr->mac_addr0_otp));
+       mac_addr1 = cpu_to_le32(iwl_read32(trans,
+                                          trans->cfg->csr->mac_addr1_otp));
 
        iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
 }
-IWL_EXPORT_SYMBOL(iwl_set_hw_address_from_csr);
 
 static void iwl_set_hw_address_family_8000(struct iwl_trans *trans,
                                           const struct iwl_cfg *cfg,
@@ -713,8 +725,8 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        struct device *dev = trans->dev;
        struct iwl_nvm_data *data;
        bool lar_enabled;
-       bool no_wide_in_5ghz = iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw);
        u32 sku, radio_cfg;
+       u32 sbands_flags = 0;
        u16 lar_config;
        const __le16 *ch_section;
 
@@ -741,8 +753,8 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                rx_chains &= data->valid_rx_ant;
 
        sku = iwl_get_sku(cfg, nvm_sw, phy_sku);
-       data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
-       data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
+       data->sku_cap_band_24ghz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
+       data->sku_cap_band_52ghz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
        data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
        if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
                data->sku_cap_11n_enable = false;
@@ -787,8 +799,14 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                return NULL;
        }
 
+       if (lar_fw_supported && lar_enabled)
+               sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR;
+
+       if (iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw))
+               sbands_flags |= IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ;
+
        iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains,
-                       lar_fw_supported && lar_enabled, no_wide_in_5ghz);
+                       sbands_flags);
        data->calib_version = 255;
 
        return data;
@@ -1017,3 +1035,294 @@ out:
        return copy_rd;
 }
 IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info);
+
+#define IWL_MAX_NVM_SECTION_SIZE       0x1b58
+#define IWL_MAX_EXT_NVM_SECTION_SIZE   0x1ffc
+#define MAX_NVM_FILE_LEN       16384
+
+void iwl_nvm_fixups(u32 hw_id, unsigned int section, u8 *data,
+                   unsigned int len)
+{
+#define IWL_4165_DEVICE_ID     0x5501
+#define NVM_SKU_CAP_MIMO_DISABLE BIT(5)
+
+       if (section == NVM_SECTION_TYPE_PHY_SKU &&
+           hw_id == IWL_4165_DEVICE_ID && data && len >= 5 &&
+           (data[4] & NVM_SKU_CAP_MIMO_DISABLE))
+               /* OTP 0x52 bug work around: it's a 1x1 device */
+               data[3] = ANT_B | (ANT_B << 4);
+}
+IWL_EXPORT_SYMBOL(iwl_nvm_fixups);
+
+/*
+ * Reads external NVM from a file into mvm->nvm_sections
+ *
+ * HOW TO CREATE THE NVM FILE FORMAT:
+ * ------------------------------
+ * 1. create hex file, format:
+ *      3800 -> header
+ *      0000 -> header
+ *      5a40 -> data
+ *
+ *   rev - 6 bit (word1)
+ *   len - 10 bit (word1)
+ *   id - 4 bit (word2)
+ *   rsv - 12 bit (word2)
+ *
+ * 2. flip 8bits with 8 bits per line to get the right NVM file format
+ *
+ * 3. create binary file from the hex file
+ *
+ * 4. save as "iNVM_xxx.bin" under /lib/firmware
+ */
+int iwl_read_external_nvm(struct iwl_trans *trans,
+                         const char *nvm_file_name,
+                         struct iwl_nvm_section *nvm_sections)
+{
+       int ret, section_size;
+       u16 section_id;
+       const struct firmware *fw_entry;
+       const struct {
+               __le16 word1;
+               __le16 word2;
+               u8 data[];
+       } *file_sec;
+       const u8 *eof;
+       u8 *temp;
+       int max_section_size;
+       const __le32 *dword_buff;
+
+#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
+#define NVM_WORD2_ID(x) (x >> 12)
+#define EXT_NVM_WORD2_LEN(x) (2 * (((x) & 0xFF) << 8 | (x) >> 8))
+#define EXT_NVM_WORD1_ID(x) ((x) >> 4)
+#define NVM_HEADER_0   (0x2A504C54)
+#define NVM_HEADER_1   (0x4E564D2A)
+#define NVM_HEADER_SIZE        (4 * sizeof(u32))
+
+       IWL_DEBUG_EEPROM(trans->dev, "Read from external NVM\n");
+
+       /* Maximal size depends on NVM version */
+       if (trans->cfg->nvm_type != IWL_NVM_EXT)
+               max_section_size = IWL_MAX_NVM_SECTION_SIZE;
+       else
+               max_section_size = IWL_MAX_EXT_NVM_SECTION_SIZE;
+
+       /*
+        * Obtain NVM image via request_firmware. Since we already used
+        * request_firmware_nowait() for the firmware binary load and only
+        * get here after that we assume the NVM request can be satisfied
+        * synchronously.
+        */
+       ret = request_firmware(&fw_entry, nvm_file_name, trans->dev);
+       if (ret) {
+               IWL_ERR(trans, "ERROR: %s isn't available %d\n",
+                       nvm_file_name, ret);
+               return ret;
+       }
+
+       IWL_INFO(trans, "Loaded NVM file %s (%zu bytes)\n",
+                nvm_file_name, fw_entry->size);
+
+       if (fw_entry->size > MAX_NVM_FILE_LEN) {
+               IWL_ERR(trans, "NVM file too large\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       eof = fw_entry->data + fw_entry->size;
+       dword_buff = (__le32 *)fw_entry->data;
+
+       /* some NVM file will contain a header.
+        * The header is identified by 2 dwords header as follow:
+        * dword[0] = 0x2A504C54
+        * dword[1] = 0x4E564D2A
+        *
+        * This header must be skipped when providing the NVM data to the FW.
+        */
+       if (fw_entry->size > NVM_HEADER_SIZE &&
+           dword_buff[0] == cpu_to_le32(NVM_HEADER_0) &&
+           dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) {
+               file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE);
+               IWL_INFO(trans, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
+               IWL_INFO(trans, "NVM Manufacturing date %08X\n",
+                        le32_to_cpu(dword_buff[3]));
+
+               /* nvm file validation, dword_buff[2] holds the file version */
+               if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+                   CSR_HW_REV_STEP(trans->hw_rev) == SILICON_C_STEP &&
+                   le32_to_cpu(dword_buff[2]) < 0xE4A) {
+                       ret = -EFAULT;
+                       goto out;
+               }
+       } else {
+               file_sec = (void *)fw_entry->data;
+       }
+
+       while (true) {
+               if (file_sec->data > eof) {
+                       IWL_ERR(trans,
+                               "ERROR - NVM file too short for section header\n");
+                       ret = -EINVAL;
+                       break;
+               }
+
+               /* check for EOF marker */
+               if (!file_sec->word1 && !file_sec->word2) {
+                       ret = 0;
+                       break;
+               }
+
+               if (trans->cfg->nvm_type != IWL_NVM_EXT) {
+                       section_size =
+                               2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1));
+                       section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2));
+               } else {
+                       section_size = 2 * EXT_NVM_WORD2_LEN(
+                                               le16_to_cpu(file_sec->word2));
+                       section_id = EXT_NVM_WORD1_ID(
+                                               le16_to_cpu(file_sec->word1));
+               }
+
+               if (section_size > max_section_size) {
+                       IWL_ERR(trans, "ERROR - section too large (%d)\n",
+                               section_size);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               if (!section_size) {
+                       IWL_ERR(trans, "ERROR - section empty\n");
+                       ret = -EINVAL;
+                       break;
+               }
+
+               if (file_sec->data + section_size > eof) {
+                       IWL_ERR(trans,
+                               "ERROR - NVM file too short for section (%d bytes)\n",
+                               section_size);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               if (WARN(section_id >= NVM_MAX_NUM_SECTIONS,
+                        "Invalid NVM section ID %d\n", section_id)) {
+                       ret = -EINVAL;
+                       break;
+               }
+
+               temp = kmemdup(file_sec->data, section_size, GFP_KERNEL);
+               if (!temp) {
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               iwl_nvm_fixups(trans->hw_id, section_id, temp, section_size);
+
+               kfree(nvm_sections[section_id].data);
+               nvm_sections[section_id].data = temp;
+               nvm_sections[section_id].length = section_size;
+
+               /* advance to the next section */
+               file_sec = (void *)(file_sec->data + section_size);
+       }
+out:
+       release_firmware(fw_entry);
+       return ret;
+}
+IWL_EXPORT_SYMBOL(iwl_read_external_nvm);
+
+struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
+                                const struct iwl_fw *fw)
+{
+       struct iwl_nvm_get_info cmd = {};
+       struct iwl_nvm_get_info_rsp *rsp;
+       struct iwl_nvm_data *nvm;
+       struct iwl_host_cmd hcmd = {
+               .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
+               .data = { &cmd, },
+               .len = { sizeof(cmd) },
+               .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, NVM_GET_INFO)
+       };
+       int  ret;
+       bool lar_fw_supported = !iwlwifi_mod_params.lar_disable &&
+                               fw_has_capa(&fw->ucode_capa,
+                                           IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
+       u32 mac_flags;
+       u32 sbands_flags = 0;
+
+       ret = iwl_trans_send_cmd(trans, &hcmd);
+       if (ret)
+               return ERR_PTR(ret);
+
+       if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp),
+                "Invalid payload len in NVM response from FW %d",
+                iwl_rx_packet_payload_len(hcmd.resp_pkt))) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       rsp = (void *)hcmd.resp_pkt->data;
+       if (le32_to_cpu(rsp->general.flags) & NVM_GENERAL_FLAGS_EMPTY_OTP)
+               IWL_INFO(trans, "OTP is empty\n");
+
+       nvm = kzalloc(sizeof(*nvm) +
+                     sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS,
+                     GFP_KERNEL);
+       if (!nvm) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       iwl_set_hw_address_from_csr(trans, nvm);
+       /* TODO: if platform NVM has MAC address - override it here */
+
+       if (!is_valid_ether_addr(nvm->hw_addr)) {
+               IWL_ERR(trans, "no valid mac address was found\n");
+               ret = -EINVAL;
+               goto err_free;
+       }
+
+       IWL_INFO(trans, "base HW address: %pM\n", nvm->hw_addr);
+
+       /* Initialize general data */
+       nvm->nvm_version = le16_to_cpu(rsp->general.nvm_version);
+
+       /* Initialize MAC sku data */
+       mac_flags = le32_to_cpu(rsp->mac_sku.mac_sku_flags);
+       nvm->sku_cap_11ac_enable =
+               !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11AC_ENABLED);
+       nvm->sku_cap_11n_enable =
+               !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11N_ENABLED);
+       nvm->sku_cap_band_24ghz_enable =
+               !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED);
+       nvm->sku_cap_band_52ghz_enable =
+               !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED);
+       nvm->sku_cap_mimo_disabled =
+               !!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED);
+
+       /* Initialize PHY sku data */
+       nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains);
+       nvm->valid_rx_ant = (u8)le32_to_cpu(rsp->phy_sku.rx_chains);
+
+       if (le32_to_cpu(rsp->regulatory.lar_enabled) && lar_fw_supported) {
+               nvm->lar_enabled = true;
+               sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR;
+       }
+
+       iwl_init_sbands(trans->dev, trans->cfg, nvm,
+                       rsp->regulatory.channel_profile,
+                       nvm->valid_tx_ant & fw->valid_tx_ant,
+                       nvm->valid_rx_ant & fw->valid_rx_ant,
+                       sbands_flags);
+
+       iwl_free_resp(&hcmd);
+       return nvm;
+
+err_free:
+       kfree(nvm);
+out:
+       iwl_free_resp(&hcmd);
+       return ERR_PTR(ret);
+}
+IWL_EXPORT_SYMBOL(iwl_get_nvm);
index 3071a23..234d100 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2008 - 2015 Intel Corporation. All rights reserved.
  * 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
@@ -33,6 +34,7 @@
  *
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * 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
 #include <net/cfg80211.h>
 #include "iwl-eeprom-parse.h"
 
+/**
+ * enum iwl_nvm_sbands_flags - modification flags for the channel profiles
+ *
+ * @IWL_NVM_SBANDS_FLAGS_LAR: LAR is enabled
+ * @IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ: disallow 40, 80 and 160MHz on 5GHz
+ */
+enum iwl_nvm_sbands_flags {
+       IWL_NVM_SBANDS_FLAGS_LAR                = BIT(0),
+       IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ    = BIT(1),
+};
+
 /**
  * iwl_parse_nvm_data - parse NVM data and return values
  *
@@ -82,20 +95,6 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                   const __le16 *mac_override, const __le16 *phy_sku,
                   u8 tx_chains, u8 rx_chains, bool lar_fw_supported);
 
-/**
- * iwl_set_hw_address_from_csr - sets HW address for 9000 devices and on
- */
-void iwl_set_hw_address_from_csr(struct iwl_trans *trans,
-                                struct iwl_nvm_data *data);
-
-/**
- * iwl_init_sbands - parse and set all channel profiles
- */
-void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
-                    struct iwl_nvm_data *data, const __le16 *nvm_ch_flags,
-                    u8 tx_chains, u8 rx_chains, bool lar_supported,
-                    bool no_wide_in_5ghz);
-
 /**
  * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW
  *
@@ -111,4 +110,33 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
                       int num_of_ch, __le32 *channels, u16 fw_mcc,
                       u16 geo_info);
 
+/**
+ * struct iwl_nvm_section - describes an NVM section in memory.
+ *
+ * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD,
+ * and saved for later use by the driver. Not all NVM sections are saved
+ * this way, only the needed ones.
+ */
+struct iwl_nvm_section {
+       u16 length;
+       const u8 *data;
+};
+
+/**
+ * iwl_read_external_nvm - Reads external NVM from a file into nvm_sections
+ */
+int iwl_read_external_nvm(struct iwl_trans *trans,
+                         const char *nvm_file_name,
+                         struct iwl_nvm_section *nvm_sections);
+void iwl_nvm_fixups(u32 hw_id, unsigned int section, u8 *data,
+                   unsigned int len);
+
+/**
+ * iwl_get_nvm - retrieve NVM data from firmware
+ *
+ * Allocates a new iwl_nvm_data structure, fills it with
+ * NVM data, and returns it to caller.
+ */
+struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
+                                const struct iwl_fw *fw);
 #endif /* __iwl_nvm_parse_h__ */
index c25ed1a..1b9c627 100644 (file)
@@ -554,7 +554,7 @@ struct iwl_trans_ops {
        /* 22000 functions */
        int (*txq_alloc)(struct iwl_trans *trans,
                         struct iwl_tx_queue_cfg_cmd *cmd,
-                        int cmd_id,
+                        int cmd_id, int size,
                         unsigned int queue_wdg_timeout);
        void (*txq_free)(struct iwl_trans *trans, int queue);
 
@@ -691,6 +691,8 @@ enum iwl_plat_pm_mode {
  * @wide_cmd_header: true when ucode supports wide command header format
  * @num_rx_queues: number of RX queues allocated by the transport;
  *     the transport must set this before calling iwl_drv_start()
+ * @iml_len: the length of the image loader
+ * @iml: a pointer to the image loader itself
  * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
  *     The user should use iwl_trans_{alloc,free}_tx_cmd.
  * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before
@@ -735,6 +737,9 @@ struct iwl_trans {
 
        u8 num_rx_queues;
 
+       size_t iml_len;
+       u8 *iml;
+
        /* The following fields are internal only */
        struct kmem_cache *dev_cmd_pool;
        char dev_cmd_pool_name[50];
@@ -952,8 +957,8 @@ iwl_trans_txq_free(struct iwl_trans *trans, int queue)
 static inline int
 iwl_trans_txq_alloc(struct iwl_trans *trans,
                    struct iwl_tx_queue_cfg_cmd *cmd,
-                   int cmd_id,
-                   unsigned int queue_wdg_timeout)
+                   int cmd_id, int size,
+                   unsigned int wdg_timeout)
 {
        might_sleep();
 
@@ -965,7 +970,7 @@ iwl_trans_txq_alloc(struct iwl_trans *trans,
                return -EIO;
        }
 
-       return trans->ops->txq_alloc(trans, cmd, cmd_id, queue_wdg_timeout);
+       return trans->ops->txq_alloc(trans, cmd, cmd_id, size, wdg_timeout);
 }
 
 static inline void iwl_trans_txq_set_shared_mode(struct iwl_trans *trans,
index 890dbff..016e03a 100644 (file)
@@ -279,6 +279,8 @@ struct iwl_bt_iterator_data {
        struct ieee80211_chanctx_conf *primary;
        struct ieee80211_chanctx_conf *secondary;
        bool primary_ll;
+       u8 primary_load;
+       u8 secondary_load;
 };
 
 static inline
@@ -295,6 +297,30 @@ void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
                enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
 }
 
+#define MVM_COEX_TCM_PERIOD (HZ * 10)
+
+static void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm *mvm,
+                                        struct iwl_bt_iterator_data *data)
+{
+       unsigned long now = jiffies;
+
+       if (!time_after(now, mvm->bt_coex_last_tcm_ts + MVM_COEX_TCM_PERIOD))
+               return;
+
+       mvm->bt_coex_last_tcm_ts = now;
+
+       /* We assume here that we don't have more than 2 vifs on 2.4GHz */
+
+       /* if the primary is low latency, it will stay primary */
+       if (data->primary_ll)
+               return;
+
+       if (data->primary_load >= data->secondary_load)
+               return;
+
+       swap(data->primary, data->secondary);
+}
+
 /* must be called under rcu_read_lock */
 static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                                      struct ieee80211_vif *vif)
@@ -385,6 +411,11 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                        /* there is low latency vif - we will be secondary */
                        data->secondary = chanctx_conf;
                }
+
+               if (data->primary == chanctx_conf)
+                       data->primary_load = mvm->tcm.result.load[mvmvif->id];
+               else if (data->secondary == chanctx_conf)
+                       data->secondary_load = mvm->tcm.result.load[mvmvif->id];
                return;
        }
 
@@ -398,6 +429,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                /* if secondary is not NULL, it might be a GO */
                data->secondary = chanctx_conf;
 
+       if (data->primary == chanctx_conf)
+               data->primary_load = mvm->tcm.result.load[mvmvif->id];
+       else if (data->secondary == chanctx_conf)
+               data->secondary_load = mvm->tcm.result.load[mvmvif->id];
        /*
         * don't reduce the Tx power if one of these is true:
         *  we are in LOOSE
@@ -449,6 +484,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
                                        iwl_mvm_bt_notif_iterator, &data);
 
+       iwl_mvm_bt_coex_tcm_based_ci(mvm, &data);
+
        if (data.primary) {
                struct ieee80211_chanctx_conf *chan = data.primary;
                if (WARN_ON(!chan->def.chan)) {
index 96b52a2..d61ff66 100644 (file)
@@ -69,6 +69,8 @@
 
 #include <linux/ieee80211.h>
 
+#define IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM         20
+
 #define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT     (100 * USEC_PER_MSEC)
 #define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT     (100 * USEC_PER_MSEC)
 #define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT      (10 * USEC_PER_MSEC)
 #define IWL_MVM_PARSE_NVM                      0
 #define IWL_MVM_ADWELL_ENABLE                  1
 #define IWL_MVM_ADWELL_MAX_BUDGET              0
+#define IWL_MVM_TCM_LOAD_MEDIUM_THRESH         10 /* percentage */
+#define IWL_MVM_TCM_LOAD_HIGH_THRESH           50 /* percentage */
+#define IWL_MVM_TCM_LOWLAT_ENABLE_THRESH       100 /* packets/10 seconds */
+#define IWL_MVM_UAPSD_NONAGG_PERIOD            5000 /* msecs */
+#define IWL_MVM_UAPSD_NOAGG_LIST_LEN           IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM
 #define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE    1
 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE      2
 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW   1
index 2efe9b0..3fcf489 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
  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
  * The full GNU General Public License is included in this distribution
  * in the file called COPYING.
  *
@@ -35,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
@@ -693,6 +690,14 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
                                    IWL_WOWLAN_WAKEUP_LINK_CHANGE);
        }
 
+       if (wowlan->any) {
+               wowlan_config_cmd->wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
+                                   IWL_WOWLAN_WAKEUP_LINK_CHANGE |
+                                   IWL_WOWLAN_WAKEUP_RX_FRAME |
+                                   IWL_WOWLAN_WAKEUP_BCN_FILTERING);
+       }
+
        return 0;
 }
 
@@ -1097,6 +1102,7 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 
        /* make sure the d0i3 exit work is not pending */
        flush_work(&mvm->d0i3_exit_work);
+       iwl_mvm_pause_tcm(mvm, true);
 
        iwl_fw_runtime_suspend(&mvm->fwrt);
 
@@ -2014,6 +2020,8 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
 
        mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
 
+       iwl_mvm_resume_tcm(mvm);
+
        iwl_fw_runtime_resume(&mvm->fwrt);
 
        return ret;
@@ -2042,6 +2050,8 @@ static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
 
        mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
 
+       iwl_mvm_pause_tcm(mvm, true);
+
        iwl_fw_runtime_suspend(&mvm->fwrt);
 
        /* start pseudo D3 */
@@ -2104,6 +2114,8 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
        __iwl_mvm_resume(mvm, true);
        rtnl_unlock();
 
+       iwl_mvm_resume_tcm(mvm);
+
        iwl_fw_runtime_resume(&mvm->fwrt);
 
        mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
index f7fcf70..798605c 100644 (file)
@@ -269,6 +269,8 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
                         mvmvif->id, mvmvif->color);
        pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
                         vif->bss_conf.bssid);
+       pos += scnprintf(buf+pos, bufsz-pos, "Load: %d\n",
+                        mvm->tcm.result.load[mvmvif->id]);
        pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n");
        for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++)
                pos += scnprintf(buf+pos, bufsz-pos,
index 0e6401c..1c4178f 100644 (file)
@@ -1728,6 +1728,27 @@ iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf,
        return ret ?: count;
 }
 
+static ssize_t
+iwl_dbgfs_uapsd_noagg_bssids_read(struct file *file, char __user *user_buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       u8 buf[IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM * ETH_ALEN * 3 + 1];
+       unsigned int pos = 0;
+       size_t bufsz = sizeof(buf);
+       int i;
+
+       mutex_lock(&mvm->mutex);
+
+       for (i = 0; i < IWL_MVM_UAPSD_NOAGG_LIST_LEN; i++)
+               pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
+                                mvm->uapsd_noagg_bssids[i].addr);
+
+       mutex_unlock(&mvm->mutex);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
 
 /* Device wide debugfs entries */
@@ -1762,6 +1783,8 @@ MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl,
                           (IWL_RSS_INDIRECTION_TABLE_SIZE * 2));
 MVM_DEBUGFS_WRITE_FILE_OPS(inject_packet, 512);
 
+MVM_DEBUGFS_READ_FILE_OPS(uapsd_noagg_bssids);
+
 #ifdef CONFIG_IWLWIFI_BCAST_FILTERING
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
@@ -1972,6 +1995,8 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
                                 mvm->debugfs_dir, &mvm->drop_bcn_ap_mode))
                goto err;
 
+       MVM_DEBUGFS_ADD_FILE(uapsd_noagg_bssids, mvm->debugfs_dir, S_IRUSR);
+
 #ifdef CONFIG_IWLWIFI_BCAST_FILTERING
        if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
                bcast_dir = debugfs_create_dir("bcast_filtering",
index 3c59109..866c91c 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
@@ -35,6 +36,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
@@ -79,6 +81,8 @@
 #include "mvm.h"
 #include "fw/dbg.h"
 #include "iwl-phy-db.h"
+#include "iwl-modparams.h"
+#include "iwl-nvm-parse.h"
 
 #define MVM_UCODE_ALIVE_TIMEOUT        HZ
 #define MVM_UCODE_CALIB_TIMEOUT        (2*HZ)
@@ -381,7 +385,8 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 
        /* Load NVM to NIC if needed */
        if (mvm->nvm_file_name) {
-               iwl_mvm_read_external_nvm(mvm);
+               iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name,
+                                     mvm->nvm_sections);
                iwl_mvm_load_nvm_to_nic(mvm);
        }
 
@@ -410,7 +415,7 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 
        /* Read the NVM only at driver load time, no need to do this twice */
        if (!IWL_MVM_PARSE_NVM && read_nvm) {
-               mvm->nvm_data = iwl_fw_get_nvm(&mvm->fwrt);
+               mvm->nvm_data = iwl_get_nvm(mvm->trans, mvm->fw);
                if (IS_ERR(mvm->nvm_data)) {
                        ret = PTR_ERR(mvm->nvm_data);
                        mvm->nvm_data = NULL;
@@ -1093,6 +1098,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
 
        if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
                mvm->scan_type = IWL_SCAN_TYPE_NOT_SET;
+               mvm->hb_scan_type = IWL_SCAN_TYPE_NOT_SET;
                ret = iwl_mvm_config_scan(mvm);
                if (ret)
                        goto error;
index 90f8c89..ec91dd9 100644 (file)
@@ -953,6 +953,16 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
 
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
+               if (iwl_mvm_vif_from_mac80211(vif)->ap_sta_id ==
+                               iwl_mvm_sta_from_mac80211(sta)->sta_id) {
+                       struct iwl_mvm_vif *mvmvif;
+                       u16 macid = iwl_mvm_vif_from_mac80211(vif)->id;
+                       struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[macid];
+
+                       mdata->opened_rx_ba_sessions = true;
+                       mvmvif = iwl_mvm_vif_from_mac80211(vif);
+                       cancel_delayed_work(&mvmvif->uapsd_nonagg_detected_wk);
+               }
                if (!iwl_enable_rx_ampdu(mvm->cfg)) {
                        ret = -EINVAL;
                        break;
@@ -1436,6 +1446,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                mvm->p2p_device_vif = vif;
        }
 
+       iwl_mvm_tcm_add_vif(mvm, vif);
+
        if (vif->type == NL80211_IFTYPE_MONITOR)
                mvm->monitor_on = true;
 
@@ -1487,6 +1499,10 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
 
        iwl_mvm_prepare_mac_removal(mvm, vif);
 
+       if (!(vif->type == NL80211_IFTYPE_AP ||
+             vif->type == NL80211_IFTYPE_ADHOC))
+               iwl_mvm_tcm_rm_vif(mvm, vif);
+
        mutex_lock(&mvm->mutex);
 
        if (mvm->bf_allowed_vif == mvmvif) {
@@ -2536,6 +2552,16 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
 static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                                const u8 *bssid)
 {
+       int i;
+
+       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+               struct iwl_mvm_tcm_mac *mdata;
+
+               mdata = &mvm->tcm.data[iwl_mvm_vif_from_mac80211(vif)->id];
+               ewma_rate_init(&mdata->uapsd_nonagg_detect.rate);
+               mdata->opened_rx_ba_sessions = false;
+       }
+
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT))
                return;
 
@@ -2550,6 +2576,13 @@ static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return;
        }
 
+       for (i = 0; i < IWL_MVM_UAPSD_NOAGG_LIST_LEN; i++) {
+               if (ether_addr_equal(mvm->uapsd_noagg_bssids[i].addr, bssid)) {
+                       vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
+                       return;
+               }
+       }
+
        vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
 }
 
@@ -2811,7 +2844,8 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
 }
 
 static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
-                                     struct ieee80211_vif *vif)
+                                      struct ieee80211_vif *vif,
+                                      u16 req_duration)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
@@ -2824,6 +2858,9 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
        if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX))
                return;
 
+       if (req_duration > duration)
+               duration = req_duration;
+
        mutex_lock(&mvm->mutex);
        /* Try really hard to protect the session and hear a beacon */
        iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false);
index d2cf751..6a4ba16 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
@@ -35,6 +36,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
@@ -90,7 +92,9 @@
 #include "fw/runtime.h"
 #include "fw/dbg.h"
 #include "fw/acpi.h"
-#include "fw/debugfs.h"
+#include "iwl-nvm-parse.h"
+
+#include <linux/average.h>
 
 #define IWL_MVM_MAX_ADDRESSES          5
 /* RSSI offset for WkP */
@@ -444,6 +448,8 @@ struct iwl_mvm_vif {
        /* FW identified misbehaving AP */
        u8 uapsd_misbehaving_bssid[ETH_ALEN];
 
+       struct delayed_work uapsd_nonagg_detected_wk;
+
        /* Indicates that CSA countdown may be started */
        bool csa_countdown;
        bool csa_failed;
@@ -502,18 +508,6 @@ enum iwl_mvm_sched_scan_pass_all_states {
        SCHED_SCAN_PASS_ALL_FOUND,
 };
 
-/**
- * struct iwl_nvm_section - describes an NVM section in memory.
- *
- * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD,
- * and saved for later use by the driver. Not all NVM sections are saved
- * this way, only the needed ones.
- */
-struct iwl_nvm_section {
-       u16 length;
-       const u8 *data;
-};
-
 /**
  * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure
  * @ct_kill_exit: worker to exit thermal kill
@@ -595,6 +589,53 @@ enum iwl_mvm_tdls_cs_state {
        IWL_MVM_TDLS_SW_ACTIVE,
 };
 
+enum iwl_mvm_traffic_load {
+       IWL_MVM_TRAFFIC_LOW,
+       IWL_MVM_TRAFFIC_MEDIUM,
+       IWL_MVM_TRAFFIC_HIGH,
+};
+
+DECLARE_EWMA(rate, 16, 16)
+
+struct iwl_mvm_tcm_mac {
+       struct {
+               u32 pkts[IEEE80211_NUM_ACS];
+               u32 airtime;
+       } tx;
+       struct {
+               u32 pkts[IEEE80211_NUM_ACS];
+               u32 airtime;
+               u32 last_ampdu_ref;
+       } rx;
+       struct {
+               /* track AP's transfer in client mode */
+               u64 rx_bytes;
+               struct ewma_rate rate;
+               bool detected;
+       } uapsd_nonagg_detect;
+       bool opened_rx_ba_sessions;
+};
+
+struct iwl_mvm_tcm {
+       struct delayed_work work;
+       spinlock_t lock; /* used when time elapsed */
+       unsigned long ts; /* timestamp when period ends */
+       unsigned long ll_ts;
+       unsigned long uapsd_nonagg_ts;
+       bool paused;
+       struct iwl_mvm_tcm_mac data[NUM_MAC_INDEX_DRIVER];
+       struct {
+               u32 elapsed; /* milliseconds for this TCM period */
+               u32 airtime[NUM_MAC_INDEX_DRIVER];
+               enum iwl_mvm_traffic_load load[NUM_MAC_INDEX_DRIVER];
+               enum iwl_mvm_traffic_load band_load[NUM_NL80211_BANDS];
+               enum iwl_mvm_traffic_load global_load;
+               bool low_latency[NUM_MAC_INDEX_DRIVER];
+               bool change[NUM_MAC_INDEX_DRIVER];
+               bool global_change;
+       } result;
+};
+
 /**
  * struct iwl_mvm_reorder_buffer - per ra/tid/queue reorder buffer
  * @head_sn: reorder window head sn
@@ -829,7 +870,10 @@ struct iwl_mvm {
        unsigned int scan_status;
        void *scan_cmd;
        struct iwl_mcast_filter_cmd *mcast_filter_cmd;
+       /* For CDB this is low band scan type, for non-CDB - type. */
        enum iwl_mvm_scan_type scan_type;
+       enum iwl_mvm_scan_type hb_scan_type;
+
        enum iwl_mvm_sched_scan_pass_all_states sched_scan_pass_all;
        struct delayed_work scan_timeout_dwork;
 
@@ -978,6 +1022,13 @@ struct iwl_mvm {
         */
        bool temperature_test;  /* Debug test temperature is enabled */
 
+       unsigned long bt_coex_last_tcm_ts;
+       struct iwl_mvm_tcm tcm;
+
+       u8 uapsd_noagg_bssid_write_idx;
+       struct mac_address uapsd_noagg_bssids[IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM]
+               __aligned(2);
+
        struct iwl_time_quota_cmd last_quota_cmd;
 
 #ifdef CONFIG_NL80211_TESTMODE
@@ -1293,6 +1344,16 @@ static inline bool iwl_mvm_is_cdb_supported(struct iwl_mvm *mvm)
                           IWL_UCODE_TLV_CAPA_CDB_SUPPORT);
 }
 
+static inline bool iwl_mvm_cdb_scan_api(struct iwl_mvm *mvm)
+{
+       /*
+        * TODO: should this be the same as iwl_mvm_is_cdb_supported()?
+        * but then there's a little bit of code in scan that won't make
+        * any sense...
+        */
+       return mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000;
+}
+
 static inline bool iwl_mvm_has_new_rx_stats_api(struct iwl_mvm *mvm)
 {
        return fw_has_api(&mvm->fw->ucode_capa,
@@ -1438,7 +1499,6 @@ void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm);
 /* NVM */
 int iwl_nvm_init(struct iwl_mvm *mvm);
 int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
-int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm);
 
 static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm)
 {
@@ -1771,6 +1831,8 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                              enum iwl_mvm_low_latency_cause cause);
 /* 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);
+
 /* get VMACLowLatencyMode */
 static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif)
 {
@@ -1906,6 +1968,17 @@ bool iwl_mvm_is_vif_assoc(struct iwl_mvm *mvm);
 
 void iwl_mvm_inactivity_check(struct iwl_mvm *mvm);
 
+#define MVM_TCM_PERIOD_MSEC 500
+#define MVM_TCM_PERIOD (HZ * MVM_TCM_PERIOD_MSEC / 1000)
+#define MVM_LL_PERIOD (10 * HZ)
+void iwl_mvm_tcm_work(struct work_struct *work);
+void iwl_mvm_recalc_tcm(struct iwl_mvm *mvm);
+void iwl_mvm_pause_tcm(struct iwl_mvm *mvm, bool with_cancel);
+void iwl_mvm_resume_tcm(struct iwl_mvm *mvm);
+void iwl_mvm_tcm_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_tcm_rm_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+u8 iwl_mvm_tcm_load_percentage(u32 airtime, u32 elapsed);
+
 void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
 unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif,
index 5bfe530..cf48517 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
@@ -35,6 +36,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
@@ -76,9 +78,7 @@
 #include "fw/acpi.h"
 
 /* Default NVM size to read */
-#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
-#define IWL_MAX_NVM_SECTION_SIZE       0x1b58
-#define IWL_MAX_EXT_NVM_SECTION_SIZE   0x1ffc
+#define IWL_NVM_DEFAULT_CHUNK_SIZE (2 * 1024)
 
 #define NVM_WRITE_OPCODE 1
 #define NVM_READ_OPCODE 0
@@ -229,19 +229,6 @@ static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section,
        return 0;
 }
 
-static void iwl_mvm_nvm_fixups(struct iwl_mvm *mvm, unsigned int section,
-                              u8 *data, unsigned int len)
-{
-#define IWL_4165_DEVICE_ID     0x5501
-#define NVM_SKU_CAP_MIMO_DISABLE BIT(5)
-
-       if (section == NVM_SECTION_TYPE_PHY_SKU &&
-           mvm->trans->hw_id == IWL_4165_DEVICE_ID && data && len >= 5 &&
-           (data[4] & NVM_SKU_CAP_MIMO_DISABLE))
-               /* OTP 0x52 bug work around: it's a 1x1 device */
-               data[3] = ANT_B | (ANT_B << 4);
-}
-
 /*
  * Reads an NVM section completely.
  * NICs prior to 7000 family doesn't have a real NVM, but just read
@@ -282,7 +269,7 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
                offset += ret;
        }
 
-       iwl_mvm_nvm_fixups(mvm, section, data, offset);
+       iwl_nvm_fixups(mvm->trans->hw_id, section, data, offset);
 
        IWL_DEBUG_EEPROM(mvm->trans->dev,
                         "NVM section %d read completed\n", section);
@@ -355,184 +342,6 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
                                  lar_enabled);
 }
 
-#define MAX_NVM_FILE_LEN       16384
-
-/*
- * Reads external NVM from a file into mvm->nvm_sections
- *
- * HOW TO CREATE THE NVM FILE FORMAT:
- * ------------------------------
- * 1. create hex file, format:
- *      3800 -> header
- *      0000 -> header
- *      5a40 -> data
- *
- *   rev - 6 bit (word1)
- *   len - 10 bit (word1)
- *   id - 4 bit (word2)
- *   rsv - 12 bit (word2)
- *
- * 2. flip 8bits with 8 bits per line to get the right NVM file format
- *
- * 3. create binary file from the hex file
- *
- * 4. save as "iNVM_xxx.bin" under /lib/firmware
- */
-int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
-{
-       int ret, section_size;
-       u16 section_id;
-       const struct firmware *fw_entry;
-       const struct {
-               __le16 word1;
-               __le16 word2;
-               u8 data[];
-       } *file_sec;
-       const u8 *eof;
-       u8 *temp;
-       int max_section_size;
-       const __le32 *dword_buff;
-
-#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
-#define NVM_WORD2_ID(x) (x >> 12)
-#define EXT_NVM_WORD2_LEN(x) (2 * (((x) & 0xFF) << 8 | (x) >> 8))
-#define EXT_NVM_WORD1_ID(x) ((x) >> 4)
-#define NVM_HEADER_0   (0x2A504C54)
-#define NVM_HEADER_1   (0x4E564D2A)
-#define NVM_HEADER_SIZE        (4 * sizeof(u32))
-
-       IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
-
-       /* Maximal size depends on NVM version */
-       if (mvm->trans->cfg->nvm_type != IWL_NVM_EXT)
-               max_section_size = IWL_MAX_NVM_SECTION_SIZE;
-       else
-               max_section_size = IWL_MAX_EXT_NVM_SECTION_SIZE;
-
-       /*
-        * Obtain NVM image via request_firmware. Since we already used
-        * request_firmware_nowait() for the firmware binary load and only
-        * get here after that we assume the NVM request can be satisfied
-        * synchronously.
-        */
-       ret = request_firmware(&fw_entry, mvm->nvm_file_name,
-                              mvm->trans->dev);
-       if (ret) {
-               IWL_ERR(mvm, "ERROR: %s isn't available %d\n",
-                       mvm->nvm_file_name, ret);
-               return ret;
-       }
-
-       IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n",
-                mvm->nvm_file_name, fw_entry->size);
-
-       if (fw_entry->size > MAX_NVM_FILE_LEN) {
-               IWL_ERR(mvm, "NVM file too large\n");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       eof = fw_entry->data + fw_entry->size;
-       dword_buff = (__le32 *)fw_entry->data;
-
-       /* some NVM file will contain a header.
-        * The header is identified by 2 dwords header as follow:
-        * dword[0] = 0x2A504C54
-        * dword[1] = 0x4E564D2A
-        *
-        * This header must be skipped when providing the NVM data to the FW.
-        */
-       if (fw_entry->size > NVM_HEADER_SIZE &&
-           dword_buff[0] == cpu_to_le32(NVM_HEADER_0) &&
-           dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) {
-               file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE);
-               IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
-               IWL_INFO(mvm, "NVM Manufacturing date %08X\n",
-                        le32_to_cpu(dword_buff[3]));
-
-               /* nvm file validation, dword_buff[2] holds the file version */
-               if (mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
-                   CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP &&
-                   le32_to_cpu(dword_buff[2]) < 0xE4A) {
-                       ret = -EFAULT;
-                       goto out;
-               }
-       } else {
-               file_sec = (void *)fw_entry->data;
-       }
-
-       while (true) {
-               if (file_sec->data > eof) {
-                       IWL_ERR(mvm,
-                               "ERROR - NVM file too short for section header\n");
-                       ret = -EINVAL;
-                       break;
-               }
-
-               /* check for EOF marker */
-               if (!file_sec->word1 && !file_sec->word2) {
-                       ret = 0;
-                       break;
-               }
-
-               if (mvm->trans->cfg->nvm_type != IWL_NVM_EXT) {
-                       section_size =
-                               2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1));
-                       section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2));
-               } else {
-                       section_size = 2 * EXT_NVM_WORD2_LEN(
-                                               le16_to_cpu(file_sec->word2));
-                       section_id = EXT_NVM_WORD1_ID(
-                                               le16_to_cpu(file_sec->word1));
-               }
-
-               if (section_size > max_section_size) {
-                       IWL_ERR(mvm, "ERROR - section too large (%d)\n",
-                               section_size);
-                       ret = -EINVAL;
-                       break;
-               }
-
-               if (!section_size) {
-                       IWL_ERR(mvm, "ERROR - section empty\n");
-                       ret = -EINVAL;
-                       break;
-               }
-
-               if (file_sec->data + section_size > eof) {
-                       IWL_ERR(mvm,
-                               "ERROR - NVM file too short for section (%d bytes)\n",
-                               section_size);
-                       ret = -EINVAL;
-                       break;
-               }
-
-               if (WARN(section_id >= NVM_MAX_NUM_SECTIONS,
-                        "Invalid NVM section ID %d\n", section_id)) {
-                       ret = -EINVAL;
-                       break;
-               }
-
-               temp = kmemdup(file_sec->data, section_size, GFP_KERNEL);
-               if (!temp) {
-                       ret = -ENOMEM;
-                       break;
-               }
-
-               iwl_mvm_nvm_fixups(mvm, section_id, temp, section_size);
-
-               kfree(mvm->nvm_sections[section_id].data);
-               mvm->nvm_sections[section_id].data = temp;
-               mvm->nvm_sections[section_id].length = section_size;
-
-               /* advance to the next section */
-               file_sec = (void *)(file_sec->data + section_size);
-       }
-out:
-       release_firmware(fw_entry);
-       return ret;
-}
-
 /* Loads the NVM data stored in mvm->nvm_sections into the NIC */
 int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
 {
@@ -585,7 +394,7 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
                        break;
                }
 
-               iwl_mvm_nvm_fixups(mvm, section, temp, ret);
+               iwl_nvm_fixups(mvm->trans->hw_id, section, temp, ret);
 
                mvm->nvm_sections[section].data = temp;
                mvm->nvm_sections[section].length = ret;
@@ -624,14 +433,17 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
        /* Only if PNVM selected in the mod param - load external NVM  */
        if (mvm->nvm_file_name) {
                /* read External NVM file from the mod param */
-               ret = iwl_mvm_read_external_nvm(mvm);
+               ret = iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name,
+                                           mvm->nvm_sections);
                if (ret) {
                        mvm->nvm_file_name = nvm_file_C;
 
                        if ((ret == -EFAULT || ret == -ENOENT) &&
                            mvm->nvm_file_name) {
                                /* in case nvm file was failed try again */
-                               ret = iwl_mvm_read_external_nvm(mvm);
+                               ret = iwl_read_external_nvm(mvm->trans,
+                                                           mvm->nvm_file_name,
+                                                           mvm->nvm_sections);
                                if (ret)
                                        return ret;
                        } else {
index 224bfa1..ff1e518 100644 (file)
@@ -250,6 +250,9 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
        RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, RX_HANDLER_SYNC),
        RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, RX_HANDLER_SYNC),
 
+       RX_HANDLER_GRP(DATA_PATH_GROUP, TLC_MNG_UPDATE_NOTIF,
+                      iwl_mvm_tlc_update_notif, RX_HANDLER_SYNC),
+
        RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif,
                   RX_HANDLER_ASYNC_LOCKED),
        RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif,
@@ -667,6 +670,12 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
 
+       spin_lock_init(&mvm->tcm.lock);
+       INIT_DELAYED_WORK(&mvm->tcm.work, iwl_mvm_tcm_work);
+       mvm->tcm.ts = jiffies;
+       mvm->tcm.ll_ts = jiffies;
+       mvm->tcm.uapsd_nonagg_ts = jiffies;
+
        INIT_DELAYED_WORK(&mvm->cs_tx_unblock_dwork, iwl_mvm_tx_unblock_dwork);
 
        /*
@@ -730,6 +739,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
               sizeof(trans->dbg_conf_tlv));
        trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv;
 
+       trans->iml = mvm->fw->iml;
+       trans->iml_len = mvm->fw->iml_len;
+
        /* set up notification wait support */
        iwl_notification_wait_init(&mvm->notif_wait);
 
@@ -859,6 +871,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
        for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++)
                kfree(mvm->nvm_sections[i].data);
 
+       cancel_delayed_work_sync(&mvm->tcm.work);
+
        iwl_mvm_tof_clean(mvm);
 
        mutex_destroy(&mvm->mutex);
@@ -1026,8 +1040,6 @@ 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, TLC_MNG_UPDATE_NOTIF))
-               iwl_mvm_tlc_update_notif(mvm, pkt);
        else
                iwl_mvm_rx_common(mvm, rxb, pkt);
 }
@@ -1432,6 +1444,7 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
                mvm->d0i3_offloading = false;
        }
 
+       iwl_mvm_pause_tcm(mvm, true);
        /* make sure we have no running tx while configuring the seqno */
        synchronize_net();
 
@@ -1615,6 +1628,7 @@ out:
        /* the FW might have updated the regdomain */
        iwl_mvm_update_changed_regdom(mvm);
 
+       iwl_mvm_resume_tcm(mvm);
        iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK);
        mutex_unlock(&mvm->mutex);
 }
index fb57456..b8b2b81 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
@@ -65,14 +67,14 @@ static u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta)
 {
        switch (sta->bandwidth) {
        case IEEE80211_STA_RX_BW_160:
-               return IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ;
+               return IWL_TLC_MNG_CH_WIDTH_160MHZ;
        case IEEE80211_STA_RX_BW_80:
-               return IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ;
+               return IWL_TLC_MNG_CH_WIDTH_80MHZ;
        case IEEE80211_STA_RX_BW_40:
-               return IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ;
+               return IWL_TLC_MNG_CH_WIDTH_40MHZ;
        case IEEE80211_STA_RX_BW_20:
        default:
-               return IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ;
+               return IWL_TLC_MNG_CH_WIDTH_20MHZ;
        }
 }
 
@@ -85,7 +87,9 @@ static u8 rs_fw_set_active_chains(u8 chains)
        if (chains & ANT_B)
                fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK;
        if (chains & ANT_C)
-               fw_chains |= IWL_TLC_MNG_CHAIN_C_MSK;
+               WARN(false,
+                    "tlc offload doesn't support antenna C. chains: 0x%x\n",
+                    chains);
 
        return fw_chains;
 }
@@ -97,13 +101,13 @@ static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta)
        u8 supp = 0;
 
        if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
-               supp |= IWL_TLC_MNG_SGI_20MHZ_MSK;
+               supp |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ);
        if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
-               supp |= IWL_TLC_MNG_SGI_40MHZ_MSK;
+               supp |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ);
        if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80)
-               supp |= IWL_TLC_MNG_SGI_80MHZ_MSK;
+               supp |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ);
        if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160)
-               supp |= IWL_TLC_MNG_SGI_160MHZ_MSK;
+               supp |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ);
 
        return supp;
 }
@@ -114,9 +118,7 @@ static u16 rs_fw_set_config_flags(struct iwl_mvm *mvm,
        struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
        struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
        bool vht_ena = vht_cap && vht_cap->vht_supported;
-       u16 flags = IWL_TLC_MNG_CFG_FLAGS_CCK_MSK |
-                   IWL_TLC_MNG_CFG_FLAGS_DCM_MSK |
-                   IWL_TLC_MNG_CFG_FLAGS_DD_MSK;
+       u16 flags = 0;
 
        if (mvm->cfg->ht_params->stbc &&
            (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
@@ -129,16 +131,11 @@ static u16 rs_fw_set_config_flags(struct iwl_mvm *mvm,
             (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))))
                flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
 
-       if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
-           (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
-           (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE))
-               flags |= IWL_TLC_MNG_CFG_FLAGS_BF_MSK;
-
        return flags;
 }
 
 static
-int rs_fw_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap,
+int rs_fw_vht_highest_rx_mcs_index(const struct ieee80211_sta_vht_cap *vht_cap,
                                   int nss)
 {
        u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
@@ -160,15 +157,16 @@ int rs_fw_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap,
        return 0;
 }
 
-static void rs_fw_vht_set_enabled_rates(struct ieee80211_sta *sta,
-                                       struct ieee80211_sta_vht_cap *vht_cap,
-                                       struct iwl_tlc_config_cmd *cmd)
+static void
+rs_fw_vht_set_enabled_rates(const struct ieee80211_sta *sta,
+                           const struct ieee80211_sta_vht_cap *vht_cap,
+                           struct iwl_tlc_config_cmd *cmd)
 {
        u16 supp;
        int i, highest_mcs;
 
        for (i = 0; i < sta->rx_nss; i++) {
-               if (i == MAX_RS_ANT_NUM)
+               if (i == MAX_NSS)
                        break;
 
                highest_mcs = rs_fw_vht_highest_rx_mcs_index(vht_cap, i + 1);
@@ -179,7 +177,9 @@ static void rs_fw_vht_set_enabled_rates(struct ieee80211_sta *sta,
                if (sta->bandwidth == IEEE80211_STA_RX_BW_20)
                        supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9);
 
-               cmd->ht_supp_rates[i] = cpu_to_le16(supp);
+               cmd->ht_rates[i][0] = cpu_to_le16(supp);
+               if (sta->bandwidth == IEEE80211_STA_RX_BW_160)
+                       cmd->ht_rates[i][1] = cmd->ht_rates[i][0];
        }
 }
 
@@ -190,8 +190,8 @@ static void rs_fw_set_supp_rates(struct ieee80211_sta *sta,
        int i;
        unsigned long tmp;
        unsigned long supp; /* must be unsigned long for for_each_set_bit */
-       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
-       struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+       const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
 
        /* non HT rates */
        supp = 0;
@@ -199,45 +199,40 @@ static void rs_fw_set_supp_rates(struct ieee80211_sta *sta,
        for_each_set_bit(i, &tmp, BITS_PER_LONG)
                supp |= BIT(sband->bitrates[i].hw_value);
 
-       cmd->non_ht_supp_rates = cpu_to_le16(supp);
+       cmd->non_ht_rates = cpu_to_le16(supp);
        cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
 
-       /* HT/VHT rates */
        if (vht_cap && vht_cap->vht_supported) {
                cmd->mode = IWL_TLC_MNG_MODE_VHT;
                rs_fw_vht_set_enabled_rates(sta, vht_cap, cmd);
        } else if (ht_cap && ht_cap->ht_supported) {
                cmd->mode = IWL_TLC_MNG_MODE_HT;
-               cmd->ht_supp_rates[0] = cpu_to_le16(ht_cap->mcs.rx_mask[0]);
-               cmd->ht_supp_rates[1] = cpu_to_le16(ht_cap->mcs.rx_mask[1]);
+               cmd->ht_rates[0][0] = cpu_to_le16(ht_cap->mcs.rx_mask[0]);
+               cmd->ht_rates[1][0] = cpu_to_le16(ht_cap->mcs.rx_mask[1]);
        }
 }
 
-static void rs_fw_tlc_mng_notif_req_config(struct iwl_mvm *mvm, u8 sta_id)
-{
-       u32 cmd_id = iwl_cmd_id(TLC_MNG_NOTIF_REQ_CMD, DATA_PATH_GROUP, 0);
-       struct iwl_tlc_notif_req_config_cmd cfg_cmd = {
-               .sta_id = sta_id,
-               .flags = cpu_to_le16(IWL_TLC_NOTIF_INIT_RATE_MSK),
-               .interval = cpu_to_le16(IWL_TLC_NOTIF_REQ_INTERVAL),
-       };
-       int ret;
-
-       ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cfg_cmd), &cfg_cmd);
-       if (ret)
-               IWL_ERR(mvm, "Failed to send TLC notif request (%d)\n", ret);
-}
-
-void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
+void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,
+                             struct iwl_rx_cmd_buffer *rxb)
 {
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_tlc_update_notif *notif;
+       struct ieee80211_sta *sta;
        struct iwl_mvm_sta *mvmsta;
        struct iwl_lq_sta_rs_fw *lq_sta;
+       u32 flags;
 
        rcu_read_lock();
 
        notif = (void *)pkt->data;
-       mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, notif->sta_id);
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]);
+       if (IS_ERR_OR_NULL(sta)) {
+               IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n",
+                       notif->sta_id);
+               goto out;
+       }
+
+       mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
        if (!mvmsta) {
                IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n",
@@ -245,14 +240,30 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
                goto out;
        }
 
+       flags = le32_to_cpu(notif->flags);
+
        lq_sta = &mvmsta->lq_sta.rs_fw;
 
-       if (le16_to_cpu(notif->flags) & IWL_TLC_NOTIF_INIT_RATE_MSK) {
-               lq_sta->last_rate_n_flags =
-                       le32_to_cpu(notif->values[IWL_TLC_NOTIF_INIT_RATE_POS]);
+       if (flags & IWL_TLC_NOTIF_FLAG_RATE) {
+               lq_sta->last_rate_n_flags = le32_to_cpu(notif->rate);
                IWL_DEBUG_RATE(mvm, "new rate_n_flags: 0x%X\n",
                               lq_sta->last_rate_n_flags);
        }
+
+       if (flags & IWL_TLC_NOTIF_FLAG_AMSDU) {
+               u16 size = le32_to_cpu(notif->amsdu_size);
+
+               if (WARN_ON(sta->max_amsdu_len < size))
+                       goto out;
+
+               mvmsta->amsdu_enabled = le32_to_cpu(notif->amsdu_enabled);
+               mvmsta->max_amsdu_len = size;
+
+               IWL_DEBUG_RATE(mvm,
+                              "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n",
+                              le32_to_cpu(notif->amsdu_size), size,
+                              mvmsta->amsdu_enabled);
+       }
 out:
        rcu_read_unlock();
 }
@@ -267,12 +278,12 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        struct ieee80211_supported_band *sband;
        struct iwl_tlc_config_cmd cfg_cmd = {
                .sta_id = mvmsta->sta_id,
-               .max_supp_ch_width = rs_fw_bw_from_sta_bw(sta),
+               .max_ch_width = rs_fw_bw_from_sta_bw(sta),
                .flags = cpu_to_le16(rs_fw_set_config_flags(mvm, sta)),
                .chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)),
-               .max_supp_ss = sta->rx_nss,
-               .max_ampdu_cnt = cpu_to_le32(mvmsta->max_agg_bufsize),
+               .max_mpdu_len = cpu_to_le16(sta->max_amsdu_len),
                .sgi_ch_width_supp = rs_fw_sgi_cw_support(sta),
+               .amsdu = iwl_mvm_is_csum_supported(mvm),
        };
        int ret;
 
@@ -287,8 +298,6 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cfg_cmd), &cfg_cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send rate scale config (%d)\n", ret);
-
-       rs_fw_tlc_mng_notif_req_config(mvm, cfg_cmd.sta_id);
 }
 
 int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
index 5d776ec..af7bc3b 100644 (file)
@@ -1715,12 +1715,18 @@ static void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
+       /*
+        * In case TLC offload is not active amsdu_enabled is either 0xFFFF
+        * or 0, since there is no per-TID alg.
+        */
        if ((!is_vht(&tbl->rate) && !is_ht(&tbl->rate)) ||
            tbl->rate.index < IWL_RATE_MCS_5_INDEX ||
            scale_action == RS_ACTION_DOWNSCALE)
-               mvmsta->tlc_amsdu = false;
+               mvmsta->amsdu_enabled = 0;
        else
-               mvmsta->tlc_amsdu = true;
+               mvmsta->amsdu_enabled = 0xFFFF;
+
+       mvmsta->max_amsdu_len = sta->max_amsdu_len;
 }
 
 /*
@@ -3134,7 +3140,8 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        sband = hw->wiphy->bands[band];
 
        lq_sta->lq.sta_id = mvmsta->sta_id;
-       mvmsta->tlc_amsdu = false;
+       mvmsta->amsdu_enabled = 0;
+       mvmsta->max_amsdu_len = sta->max_amsdu_len;
 
        for (j = 0; j < LQ_SIZE; j++)
                rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]);
@@ -3744,7 +3751,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
                                (rate->sgi) ? "SGI" : "NGI",
                                (rate->ldpc) ? "LDPC" : "BCC",
                                (lq_sta->is_agg) ? "AGG on" : "",
-                               (mvmsta->tlc_amsdu) ? "AMSDU on" : "");
+                               (mvmsta->amsdu_enabled) ? "AMSDU on" : "");
        }
        desc += scnprintf(buff + desc, bufsz - desc, "last tx rate=0x%X\n",
                        lq_sta->last_rate_n_flags);
index fb18cb8..5e89141 100644 (file)
@@ -454,5 +454,6 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                     enum nl80211_band band);
 int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
                        bool enable);
-void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt);
+void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,
+                             struct iwl_rx_cmd_buffer *rxb);
 #endif /* __rs__ */
index d26833c..bfb1634 100644 (file)
@@ -254,6 +254,74 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
        return 0;
 }
 
+static void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm,
+                                 struct ieee80211_sta *sta,
+                                 struct ieee80211_hdr *hdr, u32 len,
+                                 struct iwl_rx_phy_info *phy_info,
+                                 u32 rate_n_flags)
+{
+       struct iwl_mvm_sta *mvmsta;
+       struct iwl_mvm_tcm_mac *mdata;
+       int mac;
+       int ac = IEEE80211_AC_BE; /* treat non-QoS as BE */
+       struct iwl_mvm_vif *mvmvif;
+       /* expected throughput in 100Kbps, single stream, 20 MHz */
+       static const u8 thresh_tpt[] = {
+               9, 18, 30, 42, 60, 78, 90, 96, 120, 135,
+       };
+       u16 thr;
+
+       if (ieee80211_is_data_qos(hdr->frame_control))
+               ac = tid_to_mac80211_ac[ieee80211_get_tid(hdr)];
+
+       mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK;
+
+       if (time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD))
+               schedule_delayed_work(&mvm->tcm.work, 0);
+       mdata = &mvm->tcm.data[mac];
+       mdata->rx.pkts[ac]++;
+
+       /* count the airtime only once for each ampdu */
+       if (mdata->rx.last_ampdu_ref != mvm->ampdu_ref) {
+               mdata->rx.last_ampdu_ref = mvm->ampdu_ref;
+               mdata->rx.airtime += le16_to_cpu(phy_info->frame_time);
+       }
+
+       if (!(rate_n_flags & (RATE_MCS_HT_MSK | RATE_MCS_VHT_MSK)))
+               return;
+
+       mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+
+       if (mdata->opened_rx_ba_sessions ||
+           mdata->uapsd_nonagg_detect.detected ||
+           (!mvmvif->queue_params[IEEE80211_AC_VO].uapsd &&
+            !mvmvif->queue_params[IEEE80211_AC_VI].uapsd &&
+            !mvmvif->queue_params[IEEE80211_AC_BE].uapsd &&
+            !mvmvif->queue_params[IEEE80211_AC_BK].uapsd) ||
+           mvmsta->sta_id != mvmvif->ap_sta_id)
+               return;
+
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               thr = thresh_tpt[rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK];
+               thr *= 1 + ((rate_n_flags & RATE_HT_MCS_NSS_MSK) >>
+                                       RATE_HT_MCS_NSS_POS);
+       } else {
+               if (WARN_ON((rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK) >=
+                               ARRAY_SIZE(thresh_tpt)))
+                       return;
+               thr = thresh_tpt[rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK];
+               thr *= 1 + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+                                       RATE_VHT_MCS_NSS_POS);
+       }
+
+       thr <<= ((rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) >>
+                               RATE_MCS_CHAN_WIDTH_POS);
+
+       mdata->uapsd_nonagg_detect.rx_bytes += len;
+       ewma_rate_add(&mdata->uapsd_nonagg_detect.rate, thr);
+}
+
 static void iwl_mvm_rx_csum(struct ieee80211_sta *sta,
                            struct sk_buff *skb,
                            u32 status)
@@ -408,6 +476,12 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
                                                        NULL);
                }
 
+               if (!mvm->tcm.paused && len >= sizeof(*hdr) &&
+                   !is_multicast_ether_addr(hdr->addr1) &&
+                   ieee80211_is_data(hdr->frame_control))
+                       iwl_mvm_rx_handle_tcm(mvm, sta, hdr, len, phy_info,
+                                             rate_n_flags);
+
                if (ieee80211_is_data(hdr->frame_control))
                        iwl_mvm_rx_csum(sta, skb, rx_pkt_status);
        }
@@ -654,7 +728,8 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
        int expected_size;
        int i;
        u8 *energy;
-       __le32 *bytes, *air_time;
+       __le32 *bytes;
+       __le32 *air_time;
        __le32 flags;
 
        if (!iwl_mvm_has_new_rx_stats_api(mvm)) {
@@ -752,6 +827,32 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
                sta->avg_energy = energy[i];
        }
        rcu_read_unlock();
+
+       /*
+        * Don't update in case the statistics are not cleared, since
+        * we will end up counting twice the same airtime, once in TCM
+        * request and once in statistics notification.
+        */
+       if (!(le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR))
+               return;
+
+       spin_lock(&mvm->tcm.lock);
+       for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) {
+               struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[i];
+               u32 airtime = le32_to_cpu(air_time[i]);
+               u32 rx_bytes = le32_to_cpu(bytes[i]);
+
+               mdata->uapsd_nonagg_detect.rx_bytes += rx_bytes;
+               if (airtime) {
+                       /* re-init every time to store rate from FW */
+                       ewma_rate_init(&mdata->uapsd_nonagg_detect.rate);
+                       ewma_rate_add(&mdata->uapsd_nonagg_detect.rate,
+                                     rx_bytes * 8 / airtime);
+               }
+
+               mdata->rx.airtime += airtime;
+       }
+       spin_unlock(&mvm->tcm.lock);
 }
 
 void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
index 4a4ccfd..bb63e75 100644 (file)
@@ -112,7 +112,7 @@ static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb,
                return -1;
 
        if (ieee80211_is_data_qos(hdr->frame_control))
-               tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+               tid = ieee80211_get_tid(hdr);
        else
                tid = 0;
 
@@ -151,17 +151,9 @@ static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr,
        unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
 
        if (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) {
+               len -= 2;
                pad_len = 2;
-
-               /*
-                * If the device inserted padding it means that (it thought)
-                * the 802.11 header wasn't a multiple of 4 bytes long. In
-                * this case, reserve two bytes at the start of the SKB to
-                * align the payload properly in case we end up copying it.
-                */
-               skb_reserve(skb, pad_len);
        }
-       len -= pad_len;
 
        /* If frame is small enough to fit in skb->head, pull it completely.
         * If not, only pull ieee80211_hdr (including crypto if present, and
@@ -347,8 +339,7 @@ static bool iwl_mvm_is_dup(struct ieee80211_sta *sta, int queue,
 
        if (ieee80211_is_data_qos(hdr->frame_control))
                /* frame has qos control */
-               tid = *ieee80211_get_qos_ctl(hdr) &
-                       IEEE80211_QOS_CTL_TID_MASK;
+               tid = ieee80211_get_tid(hdr);
        else
                tid = IWL_MAX_TID_COUNT;
 
@@ -628,7 +619,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
        bool amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU;
        bool last_subframe =
                desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME;
-       u8 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+       u8 tid = ieee80211_get_tid(hdr);
        u8 sub_frame_idx = desc->amsdu_info &
                           IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;
        struct iwl_mvm_reorder_buf_entry *entries;
@@ -867,6 +858,16 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                return;
        }
 
+       if (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) {
+               /*
+                * If the device inserted padding it means that (it thought)
+                * the 802.11 header wasn't a multiple of 4 bytes long. In
+                * this case, reserve two bytes at the start of the SKB to
+                * align the payload properly in case we end up copying it.
+                */
+               skb_reserve(skb, 2);
+       }
+
        rx_status = IEEE80211_SKB_RXCB(skb);
 
        if (iwl_mvm_rx_crypto(mvm, hdr, rx_status, desc,
@@ -941,6 +942,12 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
                               IWL_RX_MPDU_REORDER_BAID_MASK) >>
                               IWL_RX_MPDU_REORDER_BAID_SHIFT);
 
+               if (!mvm->tcm.paused && len >= sizeof(*hdr) &&
+                   !is_multicast_ether_addr(hdr->addr1) &&
+                   ieee80211_is_data(hdr->frame_control) &&
+                   time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD))
+                       schedule_delayed_work(&mvm->tcm.work, 0);
+
                /*
                 * We have tx blocked stations (with CS bit). If we heard
                 * frames from a blocked station on a new channel we can
index b31f0ff..4b3753d 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
@@ -19,9 +20,7 @@
  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
+ * along with this program
  *
  * The full GNU General Public License is included in this distribution
  * in the file called COPYING.
 #define IWL_DENSE_EBS_SCAN_RATIO 5
 #define IWL_SPARSE_EBS_SCAN_RATIO 1
 
-enum iwl_mvm_traffic_load {
-       IWL_MVM_TRAFFIC_LOW,
-       IWL_MVM_TRAFFIC_MEDIUM,
-       IWL_MVM_TRAFFIC_HIGH,
-};
-
 #define IWL_SCAN_DWELL_ACTIVE          10
 #define IWL_SCAN_DWELL_PASSIVE         110
 #define IWL_SCAN_DWELL_FRAGMENTED      44
@@ -123,7 +116,9 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = {
 };
 
 struct iwl_mvm_scan_params {
+       /* For CDB this is low band scan type, for non-CDB - type. */
        enum iwl_mvm_scan_type type;
+       enum iwl_mvm_scan_type hb_type;
        u32 n_channels;
        u16 delay;
        int n_ssids;
@@ -152,7 +147,7 @@ static inline void *iwl_mvm_get_scan_req_umac_data(struct iwl_mvm *mvm)
        if (iwl_mvm_is_adaptive_dwell_supported(mvm))
                return (void *)&cmd->v7.data;
 
-       if (iwl_mvm_has_new_tx_api(mvm))
+       if (iwl_mvm_cdb_scan_api(mvm))
                return (void *)&cmd->v6.data;
 
        return (void *)&cmd->v1.data;
@@ -169,7 +164,7 @@ iwl_mvm_get_scan_req_umac_channel(struct iwl_mvm *mvm)
        if (iwl_mvm_is_adaptive_dwell_supported(mvm))
                return &cmd->v7.channel;
 
-       if (iwl_mvm_has_new_tx_api(mvm))
+       if (iwl_mvm_cdb_scan_api(mvm))
                return &cmd->v6.channel;
 
        return &cmd->v1.channel;
@@ -234,15 +229,21 @@ static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac,
 
 static enum iwl_mvm_traffic_load iwl_mvm_get_traffic_load(struct iwl_mvm *mvm)
 {
-       return IWL_MVM_TRAFFIC_LOW;
+       return mvm->tcm.result.global_load;
+}
+
+static enum iwl_mvm_traffic_load
+iwl_mvm_get_traffic_load_band(struct iwl_mvm *mvm, enum nl80211_band band)
+{
+       return mvm->tcm.result.band_load[band];
 }
 
 static enum
-iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device)
+iwl_mvm_scan_type _iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device,
+                                        enum iwl_mvm_traffic_load load,
+                                        bool low_latency)
 {
        int global_cnt = 0;
-       enum iwl_mvm_traffic_load load;
-       bool low_latency;
 
        ieee80211_iterate_active_interfaces_atomic(mvm->hw,
                                            IEEE80211_IFACE_ITER_NORMAL,
@@ -251,9 +252,6 @@ iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device)
        if (!global_cnt)
                return IWL_SCAN_TYPE_UNASSOC;
 
-       load = iwl_mvm_get_traffic_load(mvm);
-       low_latency = iwl_mvm_low_latency(mvm);
-
        if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && !p2p_device &&
            fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAGMENTED_SCAN))
                return IWL_SCAN_TYPE_FRAGMENTED;
@@ -264,25 +262,57 @@ iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device)
        return IWL_SCAN_TYPE_WILD;
 }
 
+static enum
+iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device)
+{
+       enum iwl_mvm_traffic_load load;
+       bool low_latency;
+
+       load = iwl_mvm_get_traffic_load(mvm);
+       low_latency = iwl_mvm_low_latency(mvm);
+
+       return _iwl_mvm_get_scan_type(mvm, p2p_device, load, low_latency);
+}
+
+static enum
+iwl_mvm_scan_type iwl_mvm_get_scan_type_band(struct iwl_mvm *mvm,
+                                            bool p2p_device,
+                                            enum nl80211_band band)
+{
+       enum iwl_mvm_traffic_load load;
+       bool low_latency;
+
+       load = iwl_mvm_get_traffic_load_band(mvm, band);
+       low_latency = iwl_mvm_low_latency_band(mvm, band);
+
+       return _iwl_mvm_get_scan_type(mvm, p2p_device, load, low_latency);
+}
+
 static int
 iwl_mvm_get_measurement_dwell(struct iwl_mvm *mvm,
                              struct cfg80211_scan_request *req,
                              struct iwl_mvm_scan_params *params)
 {
+       u32 duration = scan_timing[params->type].max_out_time;
+
        if (!req->duration)
                return 0;
 
-       if (req->duration_mandatory &&
-           req->duration > scan_timing[params->type].max_out_time) {
+       if (iwl_mvm_is_cdb_supported(mvm)) {
+               u32 hb_time = scan_timing[params->hb_type].max_out_time;
+
+               duration = min_t(u32, duration, hb_time);
+       }
+
+       if (req->duration_mandatory && req->duration > duration) {
                IWL_DEBUG_SCAN(mvm,
                               "Measurement scan - too long dwell %hu (max out time %u)\n",
                               req->duration,
-                              scan_timing[params->type].max_out_time);
+                              duration);
                return -EOPNOTSUPP;
        }
 
-       return min_t(u32, (u32)req->duration,
-                    scan_timing[params->type].max_out_time);
+       return min_t(u32, (u32)req->duration, duration);
 }
 
 static inline bool iwl_mvm_rrm_scan_needed(struct iwl_mvm *mvm)
@@ -437,6 +467,7 @@ void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm,
                ieee80211_scan_completed(mvm->hw, &info);
                iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
                cancel_delayed_work(&mvm->scan_timeout_dwork);
+               iwl_mvm_resume_tcm(mvm);
        } else {
                IWL_ERR(mvm,
                        "got scan complete notification but no scan is running\n");
@@ -1030,22 +1061,38 @@ static void iwl_mvm_fill_scan_config_v1(struct iwl_mvm *mvm, void *config,
 static void iwl_mvm_fill_scan_config(struct iwl_mvm *mvm, void *config,
                                     u32 flags, u8 channel_flags)
 {
-       enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, false);
        struct iwl_scan_config *cfg = config;
 
        cfg->flags = cpu_to_le32(flags);
        cfg->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
        cfg->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm));
        cfg->legacy_rates = iwl_mvm_scan_config_rates(mvm);
-       cfg->out_of_channel_time[0] =
-               cpu_to_le32(scan_timing[type].max_out_time);
-       cfg->suspend_time[0] = cpu_to_le32(scan_timing[type].suspend_time);
 
        if (iwl_mvm_is_cdb_supported(mvm)) {
-               cfg->suspend_time[1] =
-                       cpu_to_le32(scan_timing[type].suspend_time);
-               cfg->out_of_channel_time[1] =
+               enum iwl_mvm_scan_type lb_type, hb_type;
+
+               lb_type = iwl_mvm_get_scan_type_band(mvm, false,
+                                                    NL80211_BAND_2GHZ);
+               hb_type = iwl_mvm_get_scan_type_band(mvm, false,
+                                                    NL80211_BAND_5GHZ);
+
+               cfg->out_of_channel_time[SCAN_LB_LMAC_IDX] =
+                       cpu_to_le32(scan_timing[lb_type].max_out_time);
+               cfg->suspend_time[SCAN_LB_LMAC_IDX] =
+                       cpu_to_le32(scan_timing[lb_type].suspend_time);
+
+               cfg->out_of_channel_time[SCAN_HB_LMAC_IDX] =
+                       cpu_to_le32(scan_timing[hb_type].max_out_time);
+               cfg->suspend_time[SCAN_HB_LMAC_IDX] =
+                       cpu_to_le32(scan_timing[hb_type].suspend_time);
+       } else {
+               enum iwl_mvm_scan_type type =
+                       iwl_mvm_get_scan_type(mvm, false);
+
+               cfg->out_of_channel_time[SCAN_LB_LMAC_IDX] =
                        cpu_to_le32(scan_timing[type].max_out_time);
+               cfg->suspend_time[SCAN_LB_LMAC_IDX] =
+                       cpu_to_le32(scan_timing[type].suspend_time);
        }
 
        iwl_mvm_fill_scan_dwell(mvm, &cfg->dwell);
@@ -1065,7 +1112,8 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm)
        struct iwl_host_cmd cmd = {
                .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0),
        };
-       enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, false);
+       enum iwl_mvm_scan_type type;
+       enum iwl_mvm_scan_type hb_type = IWL_SCAN_TYPE_NOT_SET;
        int num_channels =
                mvm->nvm_data->bands[NL80211_BAND_2GHZ].n_channels +
                mvm->nvm_data->bands[NL80211_BAND_5GHZ].n_channels;
@@ -1075,10 +1123,20 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm)
        if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels))
                return -ENOBUFS;
 
-       if (type == mvm->scan_type)
-               return 0;
+       if (iwl_mvm_is_cdb_supported(mvm)) {
+               type = iwl_mvm_get_scan_type_band(mvm, false,
+                                                 NL80211_BAND_2GHZ);
+               hb_type = iwl_mvm_get_scan_type_band(mvm, false,
+                                                    NL80211_BAND_5GHZ);
+               if (type == mvm->scan_type && hb_type == mvm->hb_scan_type)
+                       return 0;
+       } else {
+               type = iwl_mvm_get_scan_type(mvm, false);
+               if (type == mvm->scan_type)
+                       return 0;
+       }
 
-       if (iwl_mvm_has_new_tx_api(mvm))
+       if (iwl_mvm_cdb_scan_api(mvm))
                cmd_size = sizeof(struct iwl_scan_config);
        else
                cmd_size = sizeof(struct iwl_scan_config_v1);
@@ -1107,10 +1165,15 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm)
                        IWL_CHANNEL_FLAG_EBS_ADD |
                        IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE;
 
-       if (iwl_mvm_has_new_tx_api(mvm)) {
-               flags |= (type == IWL_SCAN_TYPE_FRAGMENTED) ?
-                        SCAN_CONFIG_FLAG_SET_LMAC2_FRAGMENTED :
-                        SCAN_CONFIG_FLAG_CLEAR_LMAC2_FRAGMENTED;
+       /*
+        * Check for fragmented scan on LMAC2 - high band.
+        * LMAC1 - low band is checked above.
+        */
+       if (iwl_mvm_cdb_scan_api(mvm)) {
+               if (iwl_mvm_is_cdb_supported(mvm))
+                       flags |= (hb_type == IWL_SCAN_TYPE_FRAGMENTED) ?
+                                SCAN_CONFIG_FLAG_SET_LMAC2_FRAGMENTED :
+                                SCAN_CONFIG_FLAG_CLEAR_LMAC2_FRAGMENTED;
                iwl_mvm_fill_scan_config(mvm, cfg, flags, channel_flags);
        } else {
                iwl_mvm_fill_scan_config_v1(mvm, cfg, flags, channel_flags);
@@ -1123,8 +1186,10 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm)
        IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n");
 
        ret = iwl_mvm_send_cmd(mvm, &cmd);
-       if (!ret)
+       if (!ret) {
                mvm->scan_type = type;
+               mvm->hb_scan_type = hb_type;
+       }
 
        kfree(cfg);
        return ret;
@@ -1178,7 +1243,7 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
                        cpu_to_le32(timing->suspend_time);
 
                if (iwl_mvm_is_cdb_supported(mvm)) {
-                       hb_timing = &scan_timing[params->type];
+                       hb_timing = &scan_timing[params->hb_type];
 
                        cmd->v7.max_out_time[SCAN_HB_LMAC_IDX] =
                                cpu_to_le32(hb_timing->max_out_time);
@@ -1208,7 +1273,7 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
                cmd->v1.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED;
 
                if (iwl_mvm_is_cdb_supported(mvm)) {
-                       hb_timing = &scan_timing[params->type];
+                       hb_timing = &scan_timing[params->hb_type];
 
                        cmd->v6.max_out_time[SCAN_HB_LMAC_IDX] =
                                        cpu_to_le32(hb_timing->max_out_time);
@@ -1216,7 +1281,7 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
                                        cpu_to_le32(hb_timing->suspend_time);
                }
 
-               if (iwl_mvm_has_new_tx_api(mvm)) {
+               if (iwl_mvm_cdb_scan_api(mvm)) {
                        cmd->v6.scan_priority =
                                cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
                        cmd->v6.max_out_time[SCAN_LB_LMAC_IDX] =
@@ -1232,6 +1297,11 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
                                cpu_to_le32(timing->suspend_time);
                }
        }
+
+       if (iwl_mvm_is_regular_scan(params))
+               cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6);
+       else
+               cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_2);
 }
 
 static void
@@ -1262,11 +1332,12 @@ static u16 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,
        if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0)
                flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT;
 
-       if (params->type == IWL_SCAN_TYPE_FRAGMENTED) {
+       if (params->type == IWL_SCAN_TYPE_FRAGMENTED)
                flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED;
-               if (iwl_mvm_is_cdb_supported(mvm))
-                       flags |= IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED;
-       }
+
+       if (iwl_mvm_is_cdb_supported(mvm) &&
+           params->hb_type == IWL_SCAN_TYPE_FRAGMENTED)
+               flags |= IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED;
 
        if (iwl_mvm_rrm_scan_needed(mvm) &&
            fw_has_capa(&mvm->fw->ucode_capa,
@@ -1497,6 +1568,21 @@ void iwl_mvm_scan_timeout_wk(struct work_struct *work)
        iwl_force_nmi(mvm->trans);
 }
 
+static void iwl_mvm_fill_scan_type(struct iwl_mvm *mvm,
+                                  struct iwl_mvm_scan_params *params,
+                                  bool p2p)
+{
+       if (iwl_mvm_is_cdb_supported(mvm)) {
+               params->type =
+                       iwl_mvm_get_scan_type_band(mvm, p2p,
+                                                  NL80211_BAND_2GHZ);
+               params->hb_type =
+                       iwl_mvm_get_scan_type_band(mvm, p2p,
+                                                  NL80211_BAND_5GHZ);
+       } else {
+               params->type = iwl_mvm_get_scan_type(mvm, p2p);
+       }
+}
 int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                           struct cfg80211_scan_request *req,
                           struct ieee80211_scan_ies *ies)
@@ -1544,9 +1630,8 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        params.scan_plans = &scan_plan;
        params.n_scan_plans = 1;
 
-       params.type =
-               iwl_mvm_get_scan_type(mvm,
-                                     vif->type == NL80211_IFTYPE_P2P_DEVICE);
+       iwl_mvm_fill_scan_type(mvm, &params,
+                              vif->type == NL80211_IFTYPE_P2P_DEVICE);
 
        ret = iwl_mvm_get_measurement_dwell(mvm, req, &params);
        if (ret < 0)
@@ -1568,6 +1653,8 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        if (ret)
                return ret;
 
+       iwl_mvm_pause_tcm(mvm, false);
+
        ret = iwl_mvm_send_cmd(mvm, &hcmd);
        if (ret) {
                /* If the scan failed, it usually means that the FW was unable
@@ -1575,6 +1662,7 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                 * should try to send the command again with different params.
                 */
                IWL_ERR(mvm, "Scan failed! ret %d\n", ret);
+               iwl_mvm_resume_tcm(mvm);
                return ret;
        }
 
@@ -1638,9 +1726,8 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
        params.n_scan_plans = req->n_scan_plans;
        params.scan_plans = req->scan_plans;
 
-       params.type =
-               iwl_mvm_get_scan_type(mvm,
-                                     vif->type == NL80211_IFTYPE_P2P_DEVICE);
+       iwl_mvm_fill_scan_type(mvm, &params,
+                              vif->type == NL80211_IFTYPE_P2P_DEVICE);
 
        /* In theory, LMAC scans can handle a 32-bit delay, but since
         * waiting for over 18 hours to start the scan is a bit silly
@@ -1711,6 +1798,7 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
                mvm->scan_vif = NULL;
                iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
                cancel_delayed_work(&mvm->scan_timeout_dwork);
+               iwl_mvm_resume_tcm(mvm);
        } else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) {
                ieee80211_sched_scan_stopped(mvm->hw);
                mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED;
@@ -1827,7 +1915,7 @@ int iwl_mvm_scan_size(struct iwl_mvm *mvm)
                base_size = IWL_SCAN_REQ_UMAC_SIZE_V8;
        else if (iwl_mvm_is_adaptive_dwell_supported(mvm))
                base_size = IWL_SCAN_REQ_UMAC_SIZE_V7;
-       else if (iwl_mvm_has_new_tx_api(mvm))
+       else if (iwl_mvm_cdb_scan_api(mvm))
                base_size = IWL_SCAN_REQ_UMAC_SIZE_V6;
 
        if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
index 80067eb..4ddd2c4 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
  *
  * 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
@@ -35,6 +36,7 @@
  * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -2466,6 +2468,15 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        lockdep_assert_held(&mvm->mutex);
 
+       if (mvmsta->tid_data[tid].txq_id == IWL_MVM_INVALID_QUEUE &&
+           iwl_mvm_has_new_tx_api(mvm)) {
+               u8 ac = tid_to_mac80211_ac[tid];
+
+               ret = iwl_mvm_sta_alloc_queue_tvqm(mvm, sta, ac, tid);
+               if (ret)
+                       return ret;
+       }
+
        spin_lock_bh(&mvmsta->lock);
 
        /* possible race condition - we entered D0i3 while starting agg */
@@ -2887,7 +2898,7 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
                                u32 sta_id,
                                struct ieee80211_key_conf *key, bool mcast,
                                u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags,
-                               u8 key_offset)
+                               u8 key_offset, bool mfp)
 {
        union {
                struct iwl_mvm_add_sta_key_cmd_v1 cmd_v1;
@@ -2960,6 +2971,8 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
 
        if (mcast)
                key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
+       if (mfp)
+               key_flags |= cpu_to_le16(STA_KEY_MFP);
 
        u.cmd.common.key_offset = key_offset;
        u.cmd.common.key_flags = key_flags;
@@ -3101,11 +3114,13 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
        struct ieee80211_key_seq seq;
        u16 p1k[5];
        u32 sta_id;
+       bool mfp = false;
 
        if (sta) {
                struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 
                sta_id = mvm_sta->sta_id;
+               mfp = sta->mfp;
        } else if (vif->type == NL80211_IFTYPE_AP &&
                   !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
                struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -3127,7 +3142,8 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
                ieee80211_get_key_rx_seq(keyconf, 0, &seq);
                ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
                ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast,
-                                          seq.tkip.iv32, p1k, 0, key_offset);
+                                          seq.tkip.iv32, p1k, 0, key_offset,
+                                          mfp);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
        case WLAN_CIPHER_SUITE_WEP40:
@@ -3135,11 +3151,11 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
        case WLAN_CIPHER_SUITE_GCMP:
        case WLAN_CIPHER_SUITE_GCMP_256:
                ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast,
-                                          0, NULL, 0, key_offset);
+                                          0, NULL, 0, key_offset, mfp);
                break;
        default:
                ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast,
-                                          0, NULL, 0, key_offset);
+                                          0, NULL, 0, key_offset, mfp);
        }
 
        return ret;
@@ -3366,6 +3382,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
 {
        struct iwl_mvm_sta *mvm_sta;
        bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
+       bool mfp = sta ? sta->mfp : false;
 
        rcu_read_lock();
 
@@ -3373,7 +3390,8 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
        if (WARN_ON_ONCE(!mvm_sta))
                goto unlock;
        iwl_mvm_send_sta_key(mvm, mvm_sta->sta_id, keyconf, mcast,
-                            iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx);
+                            iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx,
+                            mfp);
 
  unlock:
        rcu_read_unlock();
index 5ffd6ad..60502c8 100644 (file)
@@ -391,7 +391,9 @@ struct iwl_mvm_rxq_dup_data {
  * @tx_protection: reference counter for controlling the Tx protection.
  * @tt_tx_protection: is thermal throttling enable Tx protection?
  * @disable_tx: is tx to this STA disabled?
- * @tlc_amsdu: true if A-MSDU is allowed
+ * @amsdu_enabled: bitmap of TX AMSDU allowed TIDs.
+ *     In case TLC offload is not active it is either 0xFFFF or 0.
+ * @max_amsdu_len: max AMSDU length
  * @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON)
  * @sleep_tx_count: the number of frames that we told the firmware to let out
  *     even when that station is asleep. This is useful in case the queue
@@ -436,7 +438,8 @@ struct iwl_mvm_sta {
        bool tt_tx_protection;
 
        bool disable_tx;
-       bool tlc_amsdu;
+       u16 amsdu_enabled;
+       u16 max_amsdu_len;
        bool sleeping;
        bool associated;
        u8 agg_tids;
index 7950659..df4c604 100644 (file)
@@ -767,16 +767,16 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
        u16 snap_ip_tcp, pad;
        unsigned int dbg_max_amsdu_len;
        netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG;
-       u8 *qc, tid, txf;
+       u8 tid, txf;
 
        snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) +
                tcp_hdrlen(skb);
 
        dbg_max_amsdu_len = READ_ONCE(mvm->max_amsdu_len);
 
-       if (!sta->max_amsdu_len ||
+       if (!mvmsta->max_amsdu_len ||
            !ieee80211_is_data_qos(hdr->frame_control) ||
-           (!mvmsta->tlc_amsdu && !dbg_max_amsdu_len))
+           (!mvmsta->amsdu_enabled && !dbg_max_amsdu_len))
                return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
 
        /*
@@ -790,8 +790,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
                return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
        }
 
-       qc = ieee80211_get_qos_ctl(hdr);
-       tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
+       tid = ieee80211_get_tid(hdr);
        if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
                return -EINVAL;
 
@@ -803,7 +802,11 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
            !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed)
                return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
 
-       max_amsdu_len = sta->max_amsdu_len;
+       if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(mvmsta->vif)) ||
+           !(mvmsta->amsdu_enabled & BIT(tid)))
+               return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
+
+       max_amsdu_len = mvmsta->max_amsdu_len;
 
        /* the Tx FIFO to which this A-MSDU will be routed */
        txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, tid_to_mac80211_ac[tid]);
@@ -841,7 +844,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
         */
        num_subframes = (max_amsdu_len + pad) / (subf_len + pad);
        if (num_subframes > 1)
-               *qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+               *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
 
        tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) -
                tcp_hdrlen(skb) + skb->data_len;
@@ -930,6 +933,32 @@ static bool iwl_mvm_txq_should_update(struct iwl_mvm *mvm, int txq_id)
        return false;
 }
 
+static void iwl_mvm_tx_airtime(struct iwl_mvm *mvm,
+                              struct iwl_mvm_sta *mvmsta,
+                              int airtime)
+{
+       int mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK;
+       struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[mac];
+
+       if (mvm->tcm.paused)
+               return;
+
+       if (time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD))
+               schedule_delayed_work(&mvm->tcm.work, 0);
+
+       mdata->tx.airtime += airtime;
+}
+
+static void iwl_mvm_tx_pkt_queued(struct iwl_mvm *mvm,
+                                 struct iwl_mvm_sta *mvmsta, int tid)
+{
+       u32 ac = tid_to_mac80211_ac[tid];
+       int mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK;
+       struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[mac];
+
+       mdata->tx.pkts[ac]++;
+}
+
 /*
  * Sets the fields in the Tx cmd that are crypto related
  */
@@ -976,9 +1005,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
         * assignment of MGMT TID
         */
        if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
-               u8 *qc = NULL;
-               qc = ieee80211_get_qos_ctl(hdr);
-               tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+               tid = ieee80211_get_tid(hdr);
                if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
                        goto drop_unlock_sta;
 
@@ -1067,6 +1094,8 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
 
        spin_unlock(&mvmsta->lock);
 
+       iwl_mvm_tx_pkt_queued(mvm, mvmsta, tid == IWL_MAX_TID_COUNT ? 0 : tid);
+
        return 0;
 
 drop_unlock_sta:
@@ -1469,6 +1498,9 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
        if (!IS_ERR(sta)) {
                mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
+               iwl_mvm_tx_airtime(mvm, mvmsta,
+                                  le16_to_cpu(tx_resp->wireless_media_time));
+
                if (tid != IWL_TID_NON_QOS && tid != IWL_MGMT_TID) {
                        struct iwl_mvm_tid_data *tid_data =
                                &mvmsta->tid_data[tid];
@@ -1610,6 +1642,8 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
                        le16_to_cpu(tx_resp->wireless_media_time);
                mvmsta->tid_data[tid].lq_color =
                        TX_RES_RATE_TABLE_COL_GET(tx_resp->tlc_info);
+               iwl_mvm_tx_airtime(mvm, mvmsta,
+                                  le16_to_cpu(tx_resp->wireless_media_time));
        }
 
        rcu_read_unlock();
@@ -1800,6 +1834,8 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
                                           le32_to_cpu(ba_res->tx_rate));
                }
 
+               iwl_mvm_tx_airtime(mvm, mvmsta,
+                                  le32_to_cpu(ba_res->wireless_time));
 out_unlock:
                rcu_read_unlock();
 out:
index d99d9ea..b002a7a 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -35,6 +36,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -278,8 +280,8 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
        u8 ind = last_idx;
        int i;
 
-       for (i = 0; i < MAX_RS_ANT_NUM; i++) {
-               ind = (ind + 1) % MAX_RS_ANT_NUM;
+       for (i = 0; i < MAX_ANT_NUM; i++) {
+               ind = (ind + 1) % MAX_ANT_NUM;
                if (valid & BIT(ind))
                        return ind;
        }
@@ -520,15 +522,15 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base)
 
                /* set INIT_DONE flag */
                iwl_set_bit(trans, CSR_GP_CNTRL,
-                           CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+                           BIT(trans->cfg->csr->flag_init_done));
 
                /* and wait for clock stabilization */
                if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
                        udelay(2);
 
                err = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                                  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
-                                  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+                                  BIT(trans->cfg->csr->flag_mac_clock_ready),
+                                  BIT(trans->cfg->csr->flag_mac_clock_ready),
                                   25000);
                if (err < 0) {
                        IWL_DEBUG_INFO(trans,
@@ -728,12 +730,14 @@ int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue,
                .sta_id = sta_id,
                .tid = tid,
        };
-       int queue;
+       int queue, size = IWL_DEFAULT_QUEUE_SIZE;
 
-       if (cmd.tid == IWL_MAX_TID_COUNT)
+       if (cmd.tid == IWL_MAX_TID_COUNT) {
                cmd.tid = IWL_MGMT_TID;
+               size = IWL_MGMT_QUEUE_SIZE;
+       }
        queue = iwl_trans_txq_alloc(mvm->trans, (void *)&cmd,
-                                   SCD_QUEUE_CFG, timeout);
+                                   SCD_QUEUE_CFG, size, timeout);
 
        if (queue < 0) {
                IWL_DEBUG_TX_QUEUES(mvm,
@@ -1074,23 +1078,48 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        return iwl_mvm_power_update_mac(mvm);
 }
 
+struct iwl_mvm_low_latency_iter {
+       bool result;
+       bool result_per_band[NUM_NL80211_BANDS];
+};
+
 static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)
 {
-       bool *result = _data;
+       struct iwl_mvm_low_latency_iter *result = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       enum nl80211_band band;
 
-       if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(vif)))
-               *result = true;
+       if (iwl_mvm_vif_low_latency(mvmvif)) {
+               result->result = true;
+
+               if (!mvmvif->phy_ctxt)
+                       return;
+
+               band = mvmvif->phy_ctxt->channel->band;
+               result->result_per_band[band] = true;
+       }
 }
 
 bool iwl_mvm_low_latency(struct iwl_mvm *mvm)
 {
-       bool result = false;
+       struct iwl_mvm_low_latency_iter data = {};
 
        ieee80211_iterate_active_interfaces_atomic(
                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
-                       iwl_mvm_ll_iter, &result);
+                       iwl_mvm_ll_iter, &data);
 
-       return result;
+       return data.result;
+}
+
+bool iwl_mvm_low_latency_band(struct iwl_mvm *mvm, enum nl80211_band band)
+{
+       struct iwl_mvm_low_latency_iter data = {};
+
+       ieee80211_iterate_active_interfaces_atomic(
+                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                       iwl_mvm_ll_iter, &data);
+
+       return data.result_per_band[band];
 }
 
 struct iwl_bss_iter_data {
@@ -1429,6 +1458,387 @@ void iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm,
                                sta->addr, tid);
 }
 
+u8 iwl_mvm_tcm_load_percentage(u32 airtime, u32 elapsed)
+{
+       if (!elapsed)
+               return 0;
+
+       return (100 * airtime / elapsed) / USEC_PER_MSEC;
+}
+
+static enum iwl_mvm_traffic_load
+iwl_mvm_tcm_load(struct iwl_mvm *mvm, u32 airtime, unsigned long elapsed)
+{
+       u8 load = iwl_mvm_tcm_load_percentage(airtime, elapsed);
+
+       if (load > IWL_MVM_TCM_LOAD_HIGH_THRESH)
+               return IWL_MVM_TRAFFIC_HIGH;
+       if (load > IWL_MVM_TCM_LOAD_MEDIUM_THRESH)
+               return IWL_MVM_TRAFFIC_MEDIUM;
+
+       return IWL_MVM_TRAFFIC_LOW;
+}
+
+struct iwl_mvm_tcm_iter_data {
+       struct iwl_mvm *mvm;
+       bool any_sent;
+};
+
+static void iwl_mvm_tcm_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_tcm_iter_data *data = _data;
+       struct iwl_mvm *mvm = data->mvm;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       bool low_latency, prev = mvmvif->low_latency & LOW_LATENCY_TRAFFIC;
+
+       if (mvmvif->id >= NUM_MAC_INDEX_DRIVER)
+               return;
+
+       low_latency = mvm->tcm.result.low_latency[mvmvif->id];
+
+       if (!mvm->tcm.result.change[mvmvif->id] &&
+           prev == low_latency) {
+               iwl_mvm_update_quotas(mvm, false, NULL);
+               return;
+       }
+
+       if (prev != low_latency) {
+               /* this sends traffic load and updates quota as well */
+               iwl_mvm_update_low_latency(mvm, vif, low_latency,
+                                          LOW_LATENCY_TRAFFIC);
+       } else {
+               iwl_mvm_update_quotas(mvm, false, NULL);
+       }
+
+       data->any_sent = true;
+}
+
+static void iwl_mvm_tcm_results(struct iwl_mvm *mvm)
+{
+       struct iwl_mvm_tcm_iter_data data = {
+               .mvm = mvm,
+               .any_sent = false,
+       };
+
+       mutex_lock(&mvm->mutex);
+
+       ieee80211_iterate_active_interfaces(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_tcm_iter, &data);
+
+       if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+               iwl_mvm_config_scan(mvm);
+
+       mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_tcm_uapsd_nonagg_detected_wk(struct work_struct *wk)
+{
+       struct iwl_mvm *mvm;
+       struct iwl_mvm_vif *mvmvif;
+       struct ieee80211_vif *vif;
+
+       mvmvif = container_of(wk, struct iwl_mvm_vif,
+                             uapsd_nonagg_detected_wk.work);
+       vif = container_of((void *)mvmvif, struct ieee80211_vif, drv_priv);
+       mvm = mvmvif->mvm;
+
+       if (mvm->tcm.data[mvmvif->id].opened_rx_ba_sessions)
+               return;
+
+       /* remember that this AP is broken */
+       memcpy(mvm->uapsd_noagg_bssids[mvm->uapsd_noagg_bssid_write_idx].addr,
+              vif->bss_conf.bssid, ETH_ALEN);
+       mvm->uapsd_noagg_bssid_write_idx++;
+       if (mvm->uapsd_noagg_bssid_write_idx >= IWL_MVM_UAPSD_NOAGG_LIST_LEN)
+               mvm->uapsd_noagg_bssid_write_idx = 0;
+
+       iwl_mvm_connection_loss(mvm, vif,
+                               "AP isn't using AMPDU with uAPSD enabled");
+}
+
+static void iwl_mvm_uapsd_agg_disconnect_iter(void *data, u8 *mac,
+                                             struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->mvm;
+       int *mac_id = data;
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       if (mvmvif->id != *mac_id)
+               return;
+
+       if (!vif->bss_conf.assoc)
+               return;
+
+       if (!mvmvif->queue_params[IEEE80211_AC_VO].uapsd &&
+           !mvmvif->queue_params[IEEE80211_AC_VI].uapsd &&
+           !mvmvif->queue_params[IEEE80211_AC_BE].uapsd &&
+           !mvmvif->queue_params[IEEE80211_AC_BK].uapsd)
+               return;
+
+       if (mvm->tcm.data[*mac_id].uapsd_nonagg_detect.detected)
+               return;
+
+       mvm->tcm.data[*mac_id].uapsd_nonagg_detect.detected = true;
+       IWL_INFO(mvm,
+                "detected AP should do aggregation but isn't, likely due to U-APSD\n");
+       schedule_delayed_work(&mvmvif->uapsd_nonagg_detected_wk, 15 * HZ);
+}
+
+static void iwl_mvm_check_uapsd_agg_expected_tpt(struct iwl_mvm *mvm,
+                                                unsigned int elapsed,
+                                                int mac)
+{
+       u64 bytes = mvm->tcm.data[mac].uapsd_nonagg_detect.rx_bytes;
+       u64 tpt;
+       unsigned long rate;
+
+       rate = ewma_rate_read(&mvm->tcm.data[mac].uapsd_nonagg_detect.rate);
+
+       if (!rate || mvm->tcm.data[mac].opened_rx_ba_sessions ||
+           mvm->tcm.data[mac].uapsd_nonagg_detect.detected)
+               return;
+
+       if (iwl_mvm_has_new_rx_api(mvm)) {
+               tpt = 8 * bytes; /* kbps */
+               do_div(tpt, elapsed);
+               rate *= 1000; /* kbps */
+               if (tpt < 22 * rate / 100)
+                       return;
+       } else {
+               /*
+                * the rate here is actually the threshold, in 100Kbps units,
+                * so do the needed conversion from bytes to 100Kbps:
+                * 100kb = bits / (100 * 1000),
+                * 100kbps = 100kb / (msecs / 1000) ==
+                *           (bits / (100 * 1000)) / (msecs / 1000) ==
+                *           bits / (100 * msecs)
+                */
+               tpt = (8 * bytes);
+               do_div(tpt, elapsed * 100);
+               if (tpt < rate)
+                       return;
+       }
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_uapsd_agg_disconnect_iter, &mac);
+}
+
+static void iwl_mvm_tcm_iterator(void *_data, u8 *mac,
+                                struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u32 *band = _data;
+
+       if (!mvmvif->phy_ctxt)
+               return;
+
+       band[mvmvif->id] = mvmvif->phy_ctxt->channel->band;
+}
+
+static unsigned long iwl_mvm_calc_tcm_stats(struct iwl_mvm *mvm,
+                                           unsigned long ts,
+                                           bool handle_uapsd)
+{
+       unsigned int elapsed = jiffies_to_msecs(ts - mvm->tcm.ts);
+       unsigned int uapsd_elapsed =
+               jiffies_to_msecs(ts - mvm->tcm.uapsd_nonagg_ts);
+       u32 total_airtime = 0;
+       u32 band_airtime[NUM_NL80211_BANDS] = {0};
+       u32 band[NUM_MAC_INDEX_DRIVER] = {0};
+       int ac, mac, i;
+       bool low_latency = false;
+       enum iwl_mvm_traffic_load load, band_load;
+       bool handle_ll = time_after(ts, mvm->tcm.ll_ts + MVM_LL_PERIOD);
+
+       if (handle_ll)
+               mvm->tcm.ll_ts = ts;
+       if (handle_uapsd)
+               mvm->tcm.uapsd_nonagg_ts = ts;
+
+       mvm->tcm.result.elapsed = elapsed;
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_tcm_iterator,
+                                                  &band);
+
+       for (mac = 0; mac < NUM_MAC_INDEX_DRIVER; mac++) {
+               struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[mac];
+               u32 vo_vi_pkts = 0;
+               u32 airtime = mdata->rx.airtime + mdata->tx.airtime;
+
+               total_airtime += airtime;
+               band_airtime[band[mac]] += airtime;
+
+               load = iwl_mvm_tcm_load(mvm, airtime, elapsed);
+               mvm->tcm.result.change[mac] = load != mvm->tcm.result.load[mac];
+               mvm->tcm.result.load[mac] = load;
+               mvm->tcm.result.airtime[mac] = airtime;
+
+               for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_VI; ac++)
+                       vo_vi_pkts += mdata->rx.pkts[ac] +
+                                     mdata->tx.pkts[ac];
+
+               /* enable immediately with enough packets but defer disabling */
+               if (vo_vi_pkts > IWL_MVM_TCM_LOWLAT_ENABLE_THRESH)
+                       mvm->tcm.result.low_latency[mac] = true;
+               else if (handle_ll)
+                       mvm->tcm.result.low_latency[mac] = false;
+
+               if (handle_ll) {
+                       /* clear old data */
+                       memset(&mdata->rx.pkts, 0, sizeof(mdata->rx.pkts));
+                       memset(&mdata->tx.pkts, 0, sizeof(mdata->tx.pkts));
+               }
+               low_latency |= mvm->tcm.result.low_latency[mac];
+
+               if (!mvm->tcm.result.low_latency[mac] && handle_uapsd)
+                       iwl_mvm_check_uapsd_agg_expected_tpt(mvm, uapsd_elapsed,
+                                                            mac);
+               /* clear old data */
+               if (handle_uapsd)
+                       mdata->uapsd_nonagg_detect.rx_bytes = 0;
+               memset(&mdata->rx.airtime, 0, sizeof(mdata->rx.airtime));
+               memset(&mdata->tx.airtime, 0, sizeof(mdata->tx.airtime));
+       }
+
+       load = iwl_mvm_tcm_load(mvm, total_airtime, elapsed);
+       mvm->tcm.result.global_change = load != mvm->tcm.result.global_load;
+       mvm->tcm.result.global_load = load;
+
+       for (i = 0; i < NUM_NL80211_BANDS; i++) {
+               band_load = iwl_mvm_tcm_load(mvm, band_airtime[i], elapsed);
+               mvm->tcm.result.band_load[i] = band_load;
+       }
+
+       /*
+        * If the current load isn't low we need to force re-evaluation
+        * in the TCM period, so that we can return to low load if there
+        * was no traffic at all (and thus iwl_mvm_recalc_tcm didn't get
+        * triggered by traffic).
+        */
+       if (load != IWL_MVM_TRAFFIC_LOW)
+               return MVM_TCM_PERIOD;
+       /*
+        * If low-latency is active we need to force re-evaluation after
+        * (the longer) MVM_LL_PERIOD, so that we can disable low-latency
+        * when there's no traffic at all.
+        */
+       if (low_latency)
+               return MVM_LL_PERIOD;
+       /*
+        * Otherwise, we don't need to run the work struct because we're
+        * in the default "idle" state - traffic indication is low (which
+        * also covers the "no traffic" case) and low-latency is disabled
+        * so there's no state that may need to be disabled when there's
+        * no traffic at all.
+        *
+        * Note that this has no impact on the regular scheduling of the
+        * updates triggered by traffic - those happen whenever one of the
+        * two timeouts expire (if there's traffic at all.)
+        */
+       return 0;
+}
+
+void iwl_mvm_recalc_tcm(struct iwl_mvm *mvm)
+{
+       unsigned long ts = jiffies;
+       bool handle_uapsd =
+               time_after(ts, mvm->tcm.uapsd_nonagg_ts +
+                              msecs_to_jiffies(IWL_MVM_UAPSD_NONAGG_PERIOD));
+
+       spin_lock(&mvm->tcm.lock);
+       if (mvm->tcm.paused || !time_after(ts, mvm->tcm.ts + MVM_TCM_PERIOD)) {
+               spin_unlock(&mvm->tcm.lock);
+               return;
+       }
+       spin_unlock(&mvm->tcm.lock);
+
+       if (handle_uapsd && iwl_mvm_has_new_rx_api(mvm)) {
+               mutex_lock(&mvm->mutex);
+               if (iwl_mvm_request_statistics(mvm, true))
+                       handle_uapsd = false;
+               mutex_unlock(&mvm->mutex);
+       }
+
+       spin_lock(&mvm->tcm.lock);
+       /* re-check if somebody else won the recheck race */
+       if (!mvm->tcm.paused && time_after(ts, mvm->tcm.ts + MVM_TCM_PERIOD)) {
+               /* calculate statistics */
+               unsigned long work_delay = iwl_mvm_calc_tcm_stats(mvm, ts,
+                                                                 handle_uapsd);
+
+               /* the memset needs to be visible before the timestamp */
+               smp_mb();
+               mvm->tcm.ts = ts;
+               if (work_delay)
+                       schedule_delayed_work(&mvm->tcm.work, work_delay);
+       }
+       spin_unlock(&mvm->tcm.lock);
+
+       iwl_mvm_tcm_results(mvm);
+}
+
+void iwl_mvm_tcm_work(struct work_struct *work)
+{
+       struct delayed_work *delayed_work = to_delayed_work(work);
+       struct iwl_mvm *mvm = container_of(delayed_work, struct iwl_mvm,
+                                          tcm.work);
+
+       iwl_mvm_recalc_tcm(mvm);
+}
+
+void iwl_mvm_pause_tcm(struct iwl_mvm *mvm, bool with_cancel)
+{
+       spin_lock_bh(&mvm->tcm.lock);
+       mvm->tcm.paused = true;
+       spin_unlock_bh(&mvm->tcm.lock);
+       if (with_cancel)
+               cancel_delayed_work_sync(&mvm->tcm.work);
+}
+
+void iwl_mvm_resume_tcm(struct iwl_mvm *mvm)
+{
+       int mac;
+
+       spin_lock_bh(&mvm->tcm.lock);
+       mvm->tcm.ts = jiffies;
+       mvm->tcm.ll_ts = jiffies;
+       for (mac = 0; mac < NUM_MAC_INDEX_DRIVER; mac++) {
+               struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[mac];
+
+               memset(&mdata->rx.pkts, 0, sizeof(mdata->rx.pkts));
+               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));
+       }
+       /* The TCM data needs to be reset before "paused" flag changes */
+       smp_mb();
+       mvm->tcm.paused = false;
+       spin_unlock_bh(&mvm->tcm.lock);
+}
+
+void iwl_mvm_tcm_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       INIT_DELAYED_WORK(&mvmvif->uapsd_nonagg_detected_wk,
+                         iwl_mvm_tcm_uapsd_nonagg_detected_wk);
+}
+
+void iwl_mvm_tcm_rm_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       cancel_delayed_work_sync(&mvmvif->uapsd_nonagg_detected_wk);
+}
+
+
 void iwl_mvm_get_sync_time(struct iwl_mvm *mvm, u32 *gp2, u64 *boottime)
 {
        bool ps_disabled;
index 5ef216f..3fc4343 100644 (file)
@@ -244,7 +244,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
        ctxt_info->hcmd_cfg.cmd_queue_addr =
                cpu_to_le64(trans_pcie->txq[trans_pcie->cmd_queue]->dma_addr);
        ctxt_info->hcmd_cfg.cmd_queue_size =
-               TFD_QUEUE_CB_SIZE(trans_pcie->tx_cmd_queue_size);
+               TFD_QUEUE_CB_SIZE(TFD_CMD_SLOTS);
 
        /* allocate ucode sections in dram and set addresses */
        ret = iwl_pcie_ctxt_info_init_fw_sec(trans, fw, ctxt_info);
index ca3b64f..45ea327 100644 (file)
@@ -383,7 +383,8 @@ struct iwl_self_init_dram {
  * @hw_init_mask: initial unmasked hw causes
  * @fh_mask: current unmasked fh causes
  * @hw_mask: current unmasked hw causes
- * @tx_cmd_queue_size: the size of the tx command queue
+ * @in_rescan: true if we have triggered a device rescan
+ * @scheduled_for_removal: true if we have scheduled a device removal
  */
 struct iwl_trans_pcie {
        struct iwl_rxq *rxq;
@@ -466,6 +467,8 @@ struct iwl_trans_pcie {
        u32 hw_mask;
        cpumask_t affinity_mask[IWL_MAX_RX_HW_QUEUES];
        u16 tx_cmd_queue_size;
+       bool in_rescan;
+       bool scheduled_for_removal;
 };
 
 static inline struct iwl_trans_pcie *
@@ -537,7 +540,6 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
 void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
                            struct sk_buff_head *skbs);
 void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
-void iwl_pcie_set_tx_cmd_queue_size(struct iwl_trans *trans);
 
 static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_trans *trans, void *_tfd,
                                          u8 idx)
@@ -822,7 +824,7 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
 void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr);
 int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans,
                                 struct iwl_tx_queue_cfg_cmd *cmd,
-                                int cmd_id,
+                                int cmd_id, int size,
                                 unsigned int timeout);
 void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue);
 int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
index f25ce3a..f772d70 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -201,7 +202,7 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
                        IWL_DEBUG_INFO(trans, "Rx queue requesting wakeup, GP1 = 0x%x\n",
                                       reg);
                        iwl_set_bit(trans, CSR_GP_CNTRL,
-                                   CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                                   BIT(trans->cfg->csr->flag_mac_access_req));
                        rxq->need_update = true;
                        return;
                }
index cb40125..b8e8dac 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
@@ -19,6 +20,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
@@ -92,7 +94,8 @@ static int iwl_pcie_gen2_apm_init(struct iwl_trans *trans)
         * Set "initialization complete" bit to move adapter from
         * D0U* --> D0A* (powered-up active) state.
         */
-       iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+       iwl_set_bit(trans, CSR_GP_CNTRL,
+                   BIT(trans->cfg->csr->flag_init_done));
 
        /*
         * Wait for clock stabilization; once stabilized, access to
@@ -100,8 +103,9 @@ static int iwl_pcie_gen2_apm_init(struct iwl_trans *trans)
         * and accesses to uCode SRAM.
         */
        ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
-                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+                          BIT(trans->cfg->csr->flag_mac_clock_ready),
+                          BIT(trans->cfg->csr->flag_mac_clock_ready),
+                          25000);
        if (ret < 0) {
                IWL_DEBUG_INFO(trans, "Failed to init the card\n");
                return ret;
@@ -143,7 +147,8 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
         * Clear "initialization complete" bit to move adapter from
         * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
         */
-       iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+       iwl_clear_bit(trans, CSR_GP_CNTRL,
+                     BIT(trans->cfg->csr->flag_init_done));
 }
 
 void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power)
@@ -187,7 +192,7 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power)
 
        /* Make sure (redundant) we've released our request to stay awake */
        iwl_clear_bit(trans, CSR_GP_CNTRL,
-                     CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                     BIT(trans->cfg->csr->flag_mac_access_req));
 
        /* Stop the device, and put it in low power state */
        iwl_pcie_gen2_apm_stop(trans, false);
index f8a0234..6e9a9ec 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
  *
  * 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
@@ -35,6 +36,7 @@
  * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -73,6 +75,7 @@
 #include <linux/gfp.h>
 #include <linux/vmalloc.h>
 #include <linux/pm_runtime.h>
+#include <linux/module.h>
 
 #include "iwl-drv.h"
 #include "iwl-trans.h"
@@ -179,7 +182,8 @@ out:
 static void iwl_trans_pcie_sw_reset(struct iwl_trans *trans)
 {
        /* Reset entire device - do controller reset (results in SHRD_HW_RST) */
-       iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+       iwl_set_bit(trans, trans->cfg->csr->addr_sw_reset,
+                   BIT(trans->cfg->csr->flag_sw_reset));
        usleep_range(5000, 6000);
 }
 
@@ -372,7 +376,8 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
         * Set "initialization complete" bit to move adapter from
         * D0U* --> D0A* (powered-up active) state.
         */
-       iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+       iwl_set_bit(trans, CSR_GP_CNTRL,
+                   BIT(trans->cfg->csr->flag_init_done));
 
        /*
         * Wait for clock stabilization; once stabilized, access to
@@ -380,8 +385,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
         * and accesses to uCode SRAM.
         */
        ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
-                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+                          BIT(trans->cfg->csr->flag_mac_clock_ready),
+                          BIT(trans->cfg->csr->flag_mac_clock_ready),
+                          25000);
        if (ret < 0) {
                IWL_ERR(trans, "Failed to init the card\n");
                return ret;
@@ -459,15 +465,16 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
         * Set "initialization complete" bit to move adapter from
         * D0U* --> D0A* (powered-up active) state.
         */
-       iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+       iwl_set_bit(trans, CSR_GP_CNTRL,
+                   BIT(trans->cfg->csr->flag_init_done));
 
        /*
         * Wait for clock stabilization; once stabilized, access to
         * device-internal resources is possible.
         */
        ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
-                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+                          BIT(trans->cfg->csr->flag_mac_clock_ready),
+                          BIT(trans->cfg->csr->flag_mac_clock_ready),
                           25000);
        if (WARN_ON(ret < 0)) {
                IWL_ERR(trans, "Access time out - failed to enable LP XTAL\n");
@@ -519,7 +526,7 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
         * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
         */
        iwl_clear_bit(trans, CSR_GP_CNTRL,
-                     CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+                     BIT(trans->cfg->csr->flag_init_done));
 
        /* Activates XTAL resources monitor */
        __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG,
@@ -541,11 +548,12 @@ void iwl_pcie_apm_stop_master(struct iwl_trans *trans)
        int ret;
 
        /* stop device's busmaster DMA activity */
-       iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
+       iwl_set_bit(trans, trans->cfg->csr->addr_sw_reset,
+                   BIT(trans->cfg->csr->flag_stop_master));
 
-       ret = iwl_poll_bit(trans, CSR_RESET,
-                          CSR_RESET_REG_FLAG_MASTER_DISABLED,
-                          CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
+       ret = iwl_poll_bit(trans, trans->cfg->csr->addr_sw_reset,
+                          BIT(trans->cfg->csr->flag_master_dis),
+                          BIT(trans->cfg->csr->flag_master_dis), 100);
        if (ret < 0)
                IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n");
 
@@ -594,7 +602,7 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
         * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
         */
        iwl_clear_bit(trans, CSR_GP_CNTRL,
-                     CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+                     BIT(trans->cfg->csr->flag_init_done));
 }
 
 static int iwl_pcie_nic_init(struct iwl_trans *trans)
@@ -1267,7 +1275,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
 
        /* Make sure (redundant) we've released our request to stay awake */
        iwl_clear_bit(trans, CSR_GP_CNTRL,
-                     CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                     BIT(trans->cfg->csr->flag_mac_access_req));
 
        /* Stop the device, and put it in low power state */
        iwl_pcie_apm_stop(trans, false);
@@ -1497,9 +1505,9 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test,
        iwl_pcie_synchronize_irqs(trans);
 
        iwl_clear_bit(trans, CSR_GP_CNTRL,
-                     CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                     BIT(trans->cfg->csr->flag_mac_access_req));
        iwl_clear_bit(trans, CSR_GP_CNTRL,
-                     CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+                     BIT(trans->cfg->csr->flag_init_done));
 
        iwl_pcie_enable_rx_wake(trans, false);
 
@@ -1543,15 +1551,17 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
                iwl_pcie_reset_ict(trans);
        iwl_enable_interrupts(trans);
 
-       iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-       iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+       iwl_set_bit(trans, CSR_GP_CNTRL,
+                   BIT(trans->cfg->csr->flag_mac_access_req));
+       iwl_set_bit(trans, CSR_GP_CNTRL,
+                   BIT(trans->cfg->csr->flag_init_done));
 
        if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000)
                udelay(2);
 
        ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
-                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+                          BIT(trans->cfg->csr->flag_mac_clock_ready),
+                          BIT(trans->cfg->csr->flag_mac_clock_ready),
                           25000);
        if (ret < 0) {
                IWL_ERR(trans, "Failed to resume the device (mac ready)\n");
@@ -1562,7 +1572,7 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
 
        if (!reset) {
                iwl_clear_bit(trans, CSR_GP_CNTRL,
-                             CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                             BIT(trans->cfg->csr->flag_mac_access_req));
        } else {
                iwl_trans_pcie_tx_reset(trans);
 
@@ -1926,6 +1936,29 @@ static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
                clear_bit(STATUS_TPOWER_PMI, &trans->status);
 }
 
+struct iwl_trans_pcie_removal {
+       struct pci_dev *pdev;
+       struct work_struct work;
+};
+
+static void iwl_trans_pcie_removal_wk(struct work_struct *wk)
+{
+       struct iwl_trans_pcie_removal *removal =
+               container_of(wk, struct iwl_trans_pcie_removal, work);
+       struct pci_dev *pdev = removal->pdev;
+       char *prop[] = {"EVENT=INACCESSIBLE", NULL};
+
+       dev_err(&pdev->dev, "Device gone - attempting removal\n");
+       kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, prop);
+       pci_lock_rescan_remove();
+       pci_dev_put(pdev);
+       pci_stop_and_remove_bus_device(pdev);
+       pci_unlock_rescan_remove();
+
+       kfree(removal);
+       module_put(THIS_MODULE);
+}
+
 static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans,
                                           unsigned long *flags)
 {
@@ -1939,7 +1972,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans,
 
        /* this bit wakes up the NIC */
        __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
-                                CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                                BIT(trans->cfg->csr->flag_mac_access_req));
        if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000)
                udelay(2);
 
@@ -1964,15 +1997,59 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans,
         * and do not save/restore SRAM when power cycling.
         */
        ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                          CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
-                          (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
+                          BIT(trans->cfg->csr->flag_val_mac_access_en),
+                          (BIT(trans->cfg->csr->flag_mac_clock_ready) |
                            CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
        if (unlikely(ret < 0)) {
-               iwl_trans_pcie_dump_regs(trans);
-               iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
+               u32 cntrl = iwl_read32(trans, CSR_GP_CNTRL);
+
                WARN_ONCE(1,
                          "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n",
-                         iwl_read32(trans, CSR_GP_CNTRL));
+                         cntrl);
+
+               iwl_trans_pcie_dump_regs(trans);
+
+               if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) {
+                       struct iwl_trans_pcie_removal *removal;
+
+                       if (trans_pcie->scheduled_for_removal)
+                               goto err;
+
+                       IWL_ERR(trans, "Device gone - scheduling removal!\n");
+
+                       /*
+                        * get a module reference to avoid doing this
+                        * while unloading anyway and to avoid
+                        * scheduling a work with code that's being
+                        * removed.
+                        */
+                       if (!try_module_get(THIS_MODULE)) {
+                               IWL_ERR(trans,
+                                       "Module is being unloaded - abort\n");
+                               goto err;
+                       }
+
+                       removal = kzalloc(sizeof(*removal), GFP_ATOMIC);
+                       if (!removal) {
+                               module_put(THIS_MODULE);
+                               goto err;
+                       }
+                       /*
+                        * we don't need to clear this flag, because
+                        * the trans will be freed and reallocated.
+                       */
+                       trans_pcie->scheduled_for_removal = true;
+
+                       removal->pdev = to_pci_dev(trans->dev);
+                       INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk);
+                       pci_dev_get(removal->pdev);
+                       schedule_work(&removal->work);
+               } else {
+                       iwl_write32(trans, CSR_RESET,
+                                   CSR_RESET_REG_FLAG_FORCE_NMI);
+               }
+
+err:
                spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
                return false;
        }
@@ -2003,7 +2080,7 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
                goto out;
 
        __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
-                                  CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                                  BIT(trans->cfg->csr->flag_mac_access_req));
        /*
         * Above we read the CSR_GP_CNTRL register, which will flush
         * any previous writes, but we need the write that clears the
@@ -3232,12 +3309,12 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
                 * id located at the AUX bus MISC address space.
                 */
                iwl_set_bit(trans, CSR_GP_CNTRL,
-                           CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+                           BIT(trans->cfg->csr->flag_init_done));
                udelay(2);
 
                ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                                  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
-                                  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+                                  BIT(trans->cfg->csr->flag_mac_clock_ready),
+                                  BIT(trans->cfg->csr->flag_mac_clock_ready),
                                   25000);
                if (ret < 0) {
                        IWL_DEBUG_INFO(trans, "Failed to wake up the nic\n");
index fabae0f..48890a1 100644 (file)
@@ -488,6 +488,23 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
 
        spin_lock(&txq->lock);
 
+       if (iwl_queue_space(txq) < txq->high_mark) {
+               iwl_stop_queue(trans, txq);
+
+               /* don't put the packet on the ring, if there is no room */
+               if (unlikely(iwl_queue_space(txq) < 3)) {
+                       struct iwl_device_cmd **dev_cmd_ptr;
+
+                       dev_cmd_ptr = (void *)((u8 *)skb->cb +
+                                              trans_pcie->dev_cmd_offs);
+
+                       *dev_cmd_ptr = dev_cmd;
+                       __skb_queue_tail(&txq->overflow_q, skb);
+                       spin_unlock(&txq->lock);
+                       return 0;
+               }
+       }
+
        idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr);
 
        /* Set up driver data for this TFD */
@@ -523,9 +540,6 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb,
        /* Tell device the write index *just past* this latest filled TFD */
        txq->write_ptr = iwl_queue_inc_wrap(txq->write_ptr);
        iwl_pcie_gen2_txq_inc_wr_ptr(trans, txq);
-       if (iwl_queue_space(txq) < txq->high_mark)
-               iwl_stop_queue(trans, txq);
-
        /*
         * At this point the frame is "transmitted" successfully
         * and we will get a TX status notification eventually.
@@ -555,15 +569,13 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
        unsigned long flags;
        void *dup_buf = NULL;
        dma_addr_t phys_addr;
-       int i, cmd_pos, idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr);
+       int i, cmd_pos, idx;
        u16 copy_size, cmd_size, tb0_size;
        bool had_nocopy = false;
        u8 group_id = iwl_cmd_groupid(cmd->id);
        const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD];
        u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD];
-       struct iwl_tfh_tfd *tfd = iwl_pcie_get_tfd(trans, txq, txq->write_ptr);
-
-       memset(tfd, 0, sizeof(*tfd));
+       struct iwl_tfh_tfd *tfd;
 
        copy_size = sizeof(struct iwl_cmd_header_wide);
        cmd_size = sizeof(struct iwl_cmd_header_wide);
@@ -634,6 +646,10 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans,
 
        spin_lock_bh(&txq->lock);
 
+       idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr);
+       tfd = iwl_pcie_get_tfd(trans, txq, txq->write_ptr);
+       memset(tfd, 0, sizeof(*tfd));
+
        if (iwl_queue_space(txq) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
                spin_unlock_bh(&txq->lock);
 
@@ -957,6 +973,13 @@ void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id)
                        spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
                }
        }
+
+       while (!skb_queue_empty(&txq->overflow_q)) {
+               struct sk_buff *skb = __skb_dequeue(&txq->overflow_q);
+
+               iwl_op_mode_free_skb(trans->op_mode, skb);
+       }
+
        spin_unlock_bh(&txq->lock);
 
        /* just in case - this queue may have been stopped */
@@ -972,7 +995,7 @@ static void iwl_pcie_gen2_txq_free_memory(struct iwl_trans *trans,
        /* De-alloc circular buffer of TFDs */
        if (txq->tfds) {
                dma_free_coherent(dev,
-                                 trans_pcie->tfd_size * TFD_QUEUE_SIZE_MAX,
+                                 trans_pcie->tfd_size * txq->n_window,
                                  txq->tfds, txq->dma_addr);
                dma_free_coherent(dev,
                                  sizeof(*txq->first_tb_bufs) * txq->n_window,
@@ -1020,7 +1043,7 @@ static void iwl_pcie_gen2_txq_free(struct iwl_trans *trans, int txq_id)
 
 int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans,
                                 struct iwl_tx_queue_cfg_cmd *cmd,
-                                int cmd_id,
+                                int cmd_id, int size,
                                 unsigned int timeout)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -1046,12 +1069,12 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans,
                return -ENOMEM;
        }
 
-       ret = iwl_pcie_txq_alloc(trans, txq, TFD_TX_CMD_SLOTS, false);
+       ret = iwl_pcie_txq_alloc(trans, txq, size, false);
        if (ret) {
                IWL_ERR(trans, "Tx queue alloc failed\n");
                goto error;
        }
-       ret = iwl_pcie_txq_init(trans, txq, TFD_TX_CMD_SLOTS, false);
+       ret = iwl_pcie_txq_init(trans, txq, size, false);
        if (ret) {
                IWL_ERR(trans, "Tx queue init failed\n");
                goto error;
@@ -1061,7 +1084,7 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans,
 
        cmd->tfdq_addr = cpu_to_le64(txq->dma_addr);
        cmd->byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma);
-       cmd->cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(TFD_TX_CMD_SLOTS));
+       cmd->cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(size));
 
        ret = iwl_trans_send_cmd(trans, &hcmd);
        if (ret)
@@ -1152,8 +1175,6 @@ int iwl_pcie_gen2_tx_init(struct iwl_trans *trans)
        struct iwl_txq *cmd_queue;
        int txq_id = trans_pcie->cmd_queue, ret;
 
-       iwl_pcie_set_tx_cmd_queue_size(trans);
-
        /* alloc and init the command queue */
        if (!trans_pcie->txq[txq_id]) {
                cmd_queue = kzalloc(sizeof(*cmd_queue), GFP_KERNEL);
@@ -1162,8 +1183,7 @@ int iwl_pcie_gen2_tx_init(struct iwl_trans *trans)
                        return -ENOMEM;
                }
                trans_pcie->txq[txq_id] = cmd_queue;
-               ret = iwl_pcie_txq_alloc(trans, cmd_queue,
-                                        trans_pcie->tx_cmd_queue_size, true);
+               ret = iwl_pcie_txq_alloc(trans, cmd_queue, TFD_CMD_SLOTS, true);
                if (ret) {
                        IWL_ERR(trans, "Tx %d queue init failed\n", txq_id);
                        goto error;
@@ -1172,8 +1192,7 @@ int iwl_pcie_gen2_tx_init(struct iwl_trans *trans)
                cmd_queue = trans_pcie->txq[txq_id];
        }
 
-       ret = iwl_pcie_txq_init(trans, cmd_queue,
-                               trans_pcie->tx_cmd_queue_size, true);
+       ret = iwl_pcie_txq_init(trans, cmd_queue, TFD_CMD_SLOTS, true);
        if (ret) {
                IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id);
                goto error;
index 1a56628..473fe7c 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2018 Intel Corporation
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -273,7 +274,7 @@ static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans,
                        IWL_DEBUG_INFO(trans, "Tx queue %d requesting wakeup, GP1 = 0x%x\n",
                                       txq_id, reg);
                        iwl_set_bit(trans, CSR_GP_CNTRL,
-                                   CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                                   BIT(trans->cfg->csr->flag_mac_access_req));
                        txq->need_update = true;
                        return;
                }
@@ -495,6 +496,9 @@ int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq,
        if (WARN_ON(txq->entries || txq->tfds))
                return -EINVAL;
 
+       if (trans->cfg->use_tfh)
+               tfd_sz = trans_pcie->tfd_size * slots_num;
+
        timer_setup(&txq->stuck_timer, iwl_pcie_txq_stuck_timer, 0);
        txq->trans_pcie = trans_pcie;
 
@@ -608,7 +612,7 @@ static void iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
 
        trans_pcie->cmd_hold_nic_awake = false;
        __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
-                                  CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                                  BIT(trans->cfg->csr->flag_mac_access_req));
 }
 
 /*
@@ -950,8 +954,7 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans)
             txq_id++) {
                bool cmd_queue = (txq_id == trans_pcie->cmd_queue);
 
-               slots_num = cmd_queue ? trans_pcie->tx_cmd_queue_size :
-                       TFD_TX_CMD_SLOTS;
+               slots_num = cmd_queue ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
                trans_pcie->txq[txq_id] = &trans_pcie->txq_memory[txq_id];
                ret = iwl_pcie_txq_alloc(trans, trans_pcie->txq[txq_id],
                                         slots_num, cmd_queue);
@@ -970,21 +973,6 @@ error:
        return ret;
 }
 
-void iwl_pcie_set_tx_cmd_queue_size(struct iwl_trans *trans)
-{
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       int queue_size = TFD_CMD_SLOTS;
-
-       if (trans->cfg->tx_cmd_queue_size)
-               queue_size = trans->cfg->tx_cmd_queue_size;
-
-       if (WARN_ON(!(is_power_of_2(queue_size) &&
-                     TFD_QUEUE_CB_SIZE(queue_size) > 0)))
-               trans_pcie->tx_cmd_queue_size = TFD_CMD_SLOTS;
-       else
-               trans_pcie->tx_cmd_queue_size = queue_size;
-}
-
 int iwl_pcie_tx_init(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -992,8 +980,6 @@ int iwl_pcie_tx_init(struct iwl_trans *trans)
        int txq_id, slots_num;
        bool alloc = false;
 
-       iwl_pcie_set_tx_cmd_queue_size(trans);
-
        if (!trans_pcie->txq_memory) {
                ret = iwl_pcie_tx_alloc(trans);
                if (ret)
@@ -1017,8 +1003,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans)
             txq_id++) {
                bool cmd_queue = (txq_id == trans_pcie->cmd_queue);
 
-               slots_num = cmd_queue ? trans_pcie->tx_cmd_queue_size :
-                       TFD_TX_CMD_SLOTS;
+               slots_num = cmd_queue ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
                ret = iwl_pcie_txq_init(trans, trans_pcie->txq[txq_id],
                                        slots_num, cmd_queue);
                if (ret) {
@@ -1166,7 +1151,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
                         * In that case, iwl_queue_space will be small again
                         * and we won't wake mac80211's queue.
                         */
-                       iwl_trans_pcie_tx(trans, skb, dev_cmd_ptr, txq_id);
+                       iwl_trans_tx(trans, skb, dev_cmd_ptr, txq_id);
                }
                spin_lock_bh(&txq->lock);
 
@@ -1187,6 +1172,7 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
                                      const struct iwl_host_cmd *cmd)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       const struct iwl_cfg *cfg = trans->cfg;
        int ret;
 
        lockdep_assert_held(&trans_pcie->reg_lock);
@@ -1204,19 +1190,19 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
         * returned. This needs to be done only on NICs that have
         * apmg_wake_up_wa set.
         */
-       if (trans->cfg->base_params->apmg_wake_up_wa &&
+       if (cfg->base_params->apmg_wake_up_wa &&
            !trans_pcie->cmd_hold_nic_awake) {
                __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
-                                        CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                                        BIT(cfg->csr->flag_mac_access_req));
 
                ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
-                                  CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
-                                  (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
+                                  BIT(cfg->csr->flag_val_mac_access_en),
+                                  (BIT(cfg->csr->flag_mac_clock_ready) |
                                    CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
                                   15000);
                if (ret < 0) {
                        __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
-                                       CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                                       BIT(cfg->csr->flag_mac_access_req));
                        IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
                        return -EIO;
                }
@@ -2411,7 +2397,13 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                goto out_err;
        iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false);
 
-       if (amsdu) {
+       /*
+        * If gso_size wasn't set, don't give the frame "amsdu treatment"
+        * (adding subframes, etc.).
+        * This can happen in some testing flows when the amsdu was already
+        * pre-built, and we just need to send the resulting skb.
+        */
+       if (amsdu && skb_shinfo(skb)->gso_size) {
                if (unlikely(iwl_fill_data_tbs_amsdu(trans, skb, txq, hdr_len,
                                                     out_meta, dev_cmd,
                                                     tb1_len)))
index 4a017a0..c26469b 100644 (file)
@@ -2650,6 +2650,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
        ieee80211_hw_set(hw, AMPDU_AGGREGATION);
        ieee80211_hw_set(hw, MFP_CAPABLE);
        ieee80211_hw_set(hw, SIGNAL_DBM);
+       ieee80211_hw_set(hw, SUPPORTS_PS);
        ieee80211_hw_set(hw, TDLS_WIDER_BW);
        if (rctbl)
                ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
index 7f7e9de..54a2297 100644 (file)
@@ -929,7 +929,7 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv,
        adapter->rx_locked = false;
        spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
 
-       mwifiex_set_mac_address(priv, dev);
+       mwifiex_set_mac_address(priv, dev, false, NULL);
 
        return 0;
 }
@@ -1979,7 +1979,8 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
                bss_cfg->bcast_ssid_ctl = 0;
                break;
        case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
-               /* firmware doesn't support this type of hidden SSID */
+               bss_cfg->bcast_ssid_ctl = 2;
+               break;
        default:
                kfree(bss_cfg);
                return -EINVAL;
@@ -2978,7 +2979,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
        priv->netdev = dev;
 
        if (!adapter->mfg_mode) {
-               mwifiex_set_mac_address(priv, dev);
+               mwifiex_set_mac_address(priv, dev, false, NULL);
 
                ret = mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE,
                                       HostCmd_ACT_GEN_SET, 0, NULL, true);
index 7014f44..9cfcdf6 100644 (file)
@@ -25,7 +25,6 @@
 #include "main.h"
 #include "wmm.h"
 #include "11n.h"
-#include "11ac.h"
 
 static void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter);
 
index 922e3d6..b10baac 100644 (file)
@@ -349,6 +349,7 @@ static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
                case WLAN_EID_SUPP_RATES:
                case WLAN_EID_COUNTRY:
                case WLAN_EID_PWR_CONSTRAINT:
+               case WLAN_EID_ERP_INFO:
                case WLAN_EID_EXT_SUPP_RATES:
                case WLAN_EID_HT_CAPABILITY:
                case WLAN_EID_HT_OPERATION:
index b648458..510f6b8 100644 (file)
@@ -858,7 +858,7 @@ mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
 /*
  * CFG802.11 network device handler for data transmission.
  */
-static int
+static netdev_tx_t
 mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
@@ -940,28 +940,32 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 }
 
 int mwifiex_set_mac_address(struct mwifiex_private *priv,
-                           struct net_device *dev)
+                           struct net_device *dev, bool external,
+                           u8 *new_mac)
 {
        int ret;
        u64 mac_addr, old_mac_addr;
 
-       if (priv->bss_type == MWIFIEX_BSS_TYPE_ANY)
-               return -ENOTSUPP;
+       old_mac_addr = ether_addr_to_u64(priv->curr_addr);
 
-       mac_addr = ether_addr_to_u64(priv->curr_addr);
-       old_mac_addr = mac_addr;
+       if (external) {
+               mac_addr = ether_addr_to_u64(new_mac);
+       } else {
+               /* Internal mac address change */
+               if (priv->bss_type == MWIFIEX_BSS_TYPE_ANY)
+                       return -ENOTSUPP;
 
-       if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
-               mac_addr |= BIT_ULL(MWIFIEX_MAC_LOCAL_ADMIN_BIT);
+               mac_addr = old_mac_addr;
 
-       if (mwifiex_get_intf_num(priv->adapter, priv->bss_type) > 1) {
-               /* Set mac address based on bss_type/bss_num */
-               mac_addr ^= BIT_ULL(priv->bss_type + 8);
-               mac_addr += priv->bss_num;
-       }
+               if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P)
+                       mac_addr |= BIT_ULL(MWIFIEX_MAC_LOCAL_ADMIN_BIT);
 
-       if (mac_addr == old_mac_addr)
-               goto done;
+               if (mwifiex_get_intf_num(priv->adapter, priv->bss_type) > 1) {
+                       /* Set mac address based on bss_type/bss_num */
+                       mac_addr ^= BIT_ULL(priv->bss_type + 8);
+                       mac_addr += priv->bss_num;
+               }
+       }
 
        u64_to_ether_addr(mac_addr, priv->curr_addr);
 
@@ -976,7 +980,6 @@ int mwifiex_set_mac_address(struct mwifiex_private *priv,
                return ret;
        }
 
-done:
        ether_addr_copy(dev->dev_addr, priv->curr_addr);
        return 0;
 }
@@ -989,8 +992,7 @@ mwifiex_ndo_set_mac_address(struct net_device *dev, void *addr)
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        struct sockaddr *hw_addr = addr;
 
-       memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN);
-       return mwifiex_set_mac_address(priv, dev);
+       return mwifiex_set_mac_address(priv, dev, true, hw_addr->sa_data);
 }
 
 /*
@@ -1331,7 +1333,10 @@ void mwifiex_init_priv_params(struct mwifiex_private *priv,
        priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK;
        priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
        priv->num_tx_timeout = 0;
-       ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr);
+       if (is_valid_ether_addr(dev->dev_addr))
+               ether_addr_copy(priv->curr_addr, dev->dev_addr);
+       else
+               ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr);
 
        if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
            GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
index 9bde181..8ae74ed 100644 (file)
@@ -84,8 +84,8 @@ enum {
 #define MWIFIEX_TIMER_10S                      10000
 #define MWIFIEX_TIMER_1S                       1000
 
-#define MAX_TX_PENDING      100
-#define LOW_TX_PENDING      80
+#define MAX_TX_PENDING      400
+#define LOW_TX_PENDING      380
 
 #define HIGH_RX_PENDING     50
 #define LOW_RX_PENDING      20
@@ -1709,7 +1709,8 @@ void mwifiex_process_multi_chan_event(struct mwifiex_private *priv,
                                      struct sk_buff *event_skb);
 void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter);
 int mwifiex_set_mac_address(struct mwifiex_private *priv,
-                           struct net_device *dev);
+                           struct net_device *dev,
+                           bool external, u8 *new_mac);
 void mwifiex_devdump_tmo_func(unsigned long function_context);
 
 #ifdef CONFIG_DEBUG_FS
index 97a6199..7538543 100644 (file)
@@ -1881,7 +1881,8 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter)
                mwifiex_dbg(adapter, EVENT,
                            "info: Event length: %d\n", evt_len);
 
-               if ((evt_len > 0) && (evt_len  < MAX_EVENT_SIZE))
+               if (evt_len > MWIFIEX_EVENT_HEADER_LEN &&
+                   evt_len < MAX_EVENT_SIZE)
                        memcpy(adapter->event_body, skb_cmd->data +
                               MWIFIEX_EVENT_HEADER_LEN, evt_len -
                               MWIFIEX_EVENT_HEADER_LEN);
index e8c8728..e86217a 100644 (file)
@@ -108,7 +108,7 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
        struct mwifiex_adapter *adapter = priv->adapter;
        int len, i;
        u32 eventcause = adapter->event_cause;
-       struct station_info sinfo;
+       struct station_info *sinfo;
        struct mwifiex_assoc_event *event;
        struct mwifiex_sta_node *node;
        u8 *deauth_mac;
@@ -117,7 +117,10 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
 
        switch (eventcause) {
        case EVENT_UAP_STA_ASSOC:
-               memset(&sinfo, 0, sizeof(sinfo));
+               sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+               if (!sinfo)
+                       return -ENOMEM;
+
                event = (struct mwifiex_assoc_event *)
                        (adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER);
                if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) {
@@ -132,28 +135,31 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
                                len = ETH_ALEN;
 
                        if (len != -1) {
-                               sinfo.assoc_req_ies = &event->data[len];
-                               len = (u8 *)sinfo.assoc_req_ies -
+                               sinfo->assoc_req_ies = &event->data[len];
+                               len = (u8 *)sinfo->assoc_req_ies -
                                      (u8 *)&event->frame_control;
-                               sinfo.assoc_req_ies_len =
+                               sinfo->assoc_req_ies_len =
                                        le16_to_cpu(event->len) - (u16)len;
                        }
                }
-               cfg80211_new_sta(priv->netdev, event->sta_addr, &sinfo,
+               cfg80211_new_sta(priv->netdev, event->sta_addr, sinfo,
                                 GFP_KERNEL);
 
                node = mwifiex_add_sta_entry(priv, event->sta_addr);
                if (!node) {
                        mwifiex_dbg(adapter, ERROR,
                                    "could not create station entry!\n");
+                       kfree(sinfo);
                        return -1;
                }
 
-               if (!priv->ap_11n_enabled)
+               if (!priv->ap_11n_enabled) {
+                       kfree(sinfo);
                        break;
+               }
 
-               mwifiex_set_sta_ht_cap(priv, sinfo.assoc_req_ies,
-                                      sinfo.assoc_req_ies_len, node);
+               mwifiex_set_sta_ht_cap(priv, sinfo->assoc_req_ies,
+                                      sinfo->assoc_req_ies_len, node);
 
                for (i = 0; i < MAX_NUM_TID; i++) {
                        if (node->is_11n_enabled)
@@ -163,6 +169,7 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
                                node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
                }
                memset(node->rx_seq, 0xff, sizeof(node->rx_seq));
+               kfree(sinfo);
                break;
        case EVENT_UAP_STA_DEAUTH:
                deauth_mac = adapter->event_body +
index fcb208d..b67acc6 100644 (file)
@@ -103,6 +103,7 @@ mt76_rx_aggr_reorder_work(struct work_struct *work)
        __skb_queue_head_init(&frames);
 
        local_bh_disable();
+       rcu_read_lock();
 
        spin_lock(&tid->lock);
        mt76_rx_aggr_check_release(tid, &frames);
@@ -114,6 +115,7 @@ mt76_rx_aggr_reorder_work(struct work_struct *work)
                                             REORDER_TIMEOUT);
        mt76_rx_complete(dev, &frames, -1);
 
+       rcu_read_unlock();
        local_bh_enable();
 }
 
@@ -147,12 +149,13 @@ mt76_rx_aggr_check_ctl(struct sk_buff *skb, struct sk_buff_head *frames)
 void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
 {
        struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct mt76_wcid *wcid = status->wcid;
        struct ieee80211_sta *sta;
        struct mt76_rx_tid *tid;
        bool sn_less;
        u16 seqno, head, size;
-       u8 idx;
+       u8 ackp, idx;
 
        __skb_queue_tail(frames, skb);
 
@@ -165,10 +168,17 @@ void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
                return;
        }
 
+       /* not part of a BA session */
+       ackp = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_ACK_POLICY_MASK;
+       if (ackp != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK &&
+           ackp != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL)
+               return;
+
        tid = rcu_dereference(wcid->aggr[status->tid]);
        if (!tid)
                return;
 
+       status->flag |= RX_FLAG_DUP_VALIDATED;
        spin_lock_bh(&tid->lock);
 
        if (tid->stopped)
@@ -258,6 +268,8 @@ static void mt76_rx_aggr_shutdown(struct mt76_dev *dev, struct mt76_rx_tid *tid)
        u8 size = tid->size;
        int i;
 
+       cancel_delayed_work(&tid->reorder_work);
+
        spin_lock_bh(&tid->lock);
 
        tid->stopped = true;
@@ -272,8 +284,6 @@ static void mt76_rx_aggr_shutdown(struct mt76_dev *dev, struct mt76_rx_tid *tid)
        }
 
        spin_unlock_bh(&tid->lock);
-
-       cancel_delayed_work_sync(&tid->reorder_work);
 }
 
 void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno)
index 4f30cdc..915e617 100644 (file)
@@ -213,7 +213,8 @@ mt76_init_sband(struct mt76_dev *dev, struct mt76_sband *msband,
        vht_cap->vht_supported = true;
        vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC |
                        IEEE80211_VHT_CAP_RXSTBC_1 |
-                       IEEE80211_VHT_CAP_SHORT_GI_80;
+                       IEEE80211_VHT_CAP_SHORT_GI_80 |
+                       (3 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT);
 
        return 0;
 }
@@ -541,15 +542,13 @@ mt76_check_ps(struct mt76_dev *dev, struct sk_buff *skb)
        if (!!test_bit(MT_WCID_FLAG_PS, &wcid->flags) == ps)
                return;
 
-       if (ps) {
+       if (ps)
                set_bit(MT_WCID_FLAG_PS, &wcid->flags);
-               mt76_stop_tx_queues(dev, sta, true);
-       } else {
+       else
                clear_bit(MT_WCID_FLAG_PS, &wcid->flags);
-       }
 
-       ieee80211_sta_ps_transition(sta, ps);
        dev->drv->sta_ps(dev, sta, ps);
+       ieee80211_sta_ps_transition(sta, ps);
 }
 
 void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
@@ -562,6 +561,7 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
        if (queue >= 0)
            napi = &dev->napi[queue];
 
+       spin_lock(&dev->rx_lock);
        while ((skb = __skb_dequeue(frames)) != NULL) {
                if (mt76_check_ccmp_pn(skb)) {
                        dev_kfree_skb(skb);
@@ -571,6 +571,7 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
                sta = mt76_rx_convert(skb);
                ieee80211_rx_napi(dev->hw, sta, skb, napi);
        }
+       spin_unlock(&dev->rx_lock);
 }
 
 void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
index 065ff78..a74e6ee 100644 (file)
@@ -241,6 +241,7 @@ struct mt76_dev {
        struct device *dev;
 
        struct net_device napi_dev;
+       spinlock_t rx_lock;
        struct napi_struct napi[__MT_RXQ_MAX];
        struct sk_buff_head rx_skb[__MT_RXQ_MAX];
 
index 783b812..a5d1255 100644 (file)
@@ -117,7 +117,6 @@ struct mt76x2_dev {
        u8 beacon_mask;
        u8 beacon_data_mask;
 
-       u32 rev;
        u32 rxfilter;
 
        u16 chainmask;
@@ -151,7 +150,7 @@ struct mt76x2_sta {
 
 static inline bool is_mt7612(struct mt76x2_dev *dev)
 {
-       return (dev->rev >> 16) == 0x7612;
+       return mt76_chip(&dev->mt76) == 0x7612;
 }
 
 void mt76x2_set_irq_mask(struct mt76x2_dev *dev, u32 clear, u32 set);
index 47f79d8..e9d426b 100644 (file)
@@ -19,7 +19,7 @@
 
 #include "dma.h"
 
-#define MT_TXD_INFO_LEN                        GENMASK(13, 0)
+#define MT_TXD_INFO_LEN                        GENMASK(15, 0)
 #define MT_TXD_INFO_NEXT_VLD           BIT(16)
 #define MT_TXD_INFO_TX_BURST           BIT(17)
 #define MT_TXD_INFO_80211              BIT(19)
@@ -27,9 +27,8 @@
 #define MT_TXD_INFO_CSO                        BIT(21)
 #define MT_TXD_INFO_WIV                        BIT(24)
 #define MT_TXD_INFO_QSEL               GENMASK(26, 25)
-#define MT_TXD_INFO_TCO                        BIT(29)
-#define MT_TXD_INFO_UCO                        BIT(30)
-#define MT_TXD_INFO_ICO                        BIT(31)
+#define MT_TXD_INFO_DPORT              GENMASK(29, 27)
+#define MT_TXD_INFO_TYPE               GENMASK(31, 30)
 
 #define MT_RX_FCE_INFO_LEN             GENMASK(13, 0)
 #define MT_RX_FCE_INFO_SELF_GEN                BIT(15)
index 5bb5002..95d5f7d 100644 (file)
@@ -609,17 +609,13 @@ int mt76x2_get_temp_comp(struct mt76x2_dev *dev, struct mt76x2_temp_comp *t)
 
        memset(t, 0, sizeof(*t));
 
-       val = mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1);
-       if (!(val & MT_EE_NIC_CONF_1_TEMP_TX_ALC))
+       if (!mt76x2_temp_tx_alc_enabled(dev))
                return -EINVAL;
 
        if (!mt76x2_ext_pa_enabled(dev, band))
                return -EINVAL;
 
        val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G) >> 8;
-       if (!(val & BIT(7)))
-               return -EINVAL;
-
        t->temp_25_ref = val & 0x7f;
        if (band == NL80211_BAND_5GHZ) {
                slope = mt76x2_eeprom_get(dev, MT_EE_RF_TEMP_COMP_SLOPE_5G);
index d791227..aa0b0c0 100644 (file)
@@ -159,6 +159,12 @@ void mt76x2_read_rx_gain(struct mt76x2_dev *dev);
 static inline bool
 mt76x2_temp_tx_alc_enabled(struct mt76x2_dev *dev)
 {
+       u16 val;
+
+       val = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G);
+       if (!(val & BIT(15)))
+               return false;
+
        return mt76x2_eeprom_get(dev, MT_EE_NIC_CONF_1) &
               MT_EE_NIC_CONF_1_TEMP_TX_ALC;
 }
index 934c331..dd4c112 100644 (file)
@@ -228,7 +228,7 @@ mt76x2_init_beacon_offsets(struct mt76x2_dev *dev)
                mt76_wr(dev, MT_BCN_OFFSET(i), regs[i]);
 }
 
-int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard)
+static int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard)
 {
        static const u8 null_addr[ETH_ALEN] = {};
        const u8 *macaddr = dev->mt76.macaddr;
@@ -370,12 +370,12 @@ void mt76x2_mac_stop(struct mt76x2_dev *dev, bool force)
 
        /* Wait for MAC to become idle */
        for (i = 0; i < 300; i++) {
-               if (mt76_rr(dev, MT_MAC_STATUS) &
-                   (MT_MAC_STATUS_RX | MT_MAC_STATUS_TX))
-                       continue;
-
-               if (mt76_rr(dev, MT_BBP(IBI, 12)))
+               if ((mt76_rr(dev, MT_MAC_STATUS) &
+                    (MT_MAC_STATUS_RX | MT_MAC_STATUS_TX)) ||
+                   mt76_rr(dev, MT_BBP(IBI, 12))) {
+                       usleep_range(10, 20);
                        continue;
+               }
 
                stopped = true;
                break;
@@ -645,6 +645,7 @@ struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev)
        dev->mt76.drv = &drv_ops;
        mutex_init(&dev->mutex);
        spin_lock_init(&dev->irq_lock);
+       spin_lock_init(&dev->mt76.rx_lock);
 
        return dev;
 }
index d183156..dab7137 100644 (file)
@@ -410,7 +410,6 @@ mt76x2_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate,
                break;
        default:
                return -EINVAL;
-               break;
        }
 
        if (rate & MT_RXWI_RATE_SGI)
index 8a8a25e..c048cd0 100644 (file)
@@ -157,7 +157,6 @@ mt76x2_skb_tx_info(struct sk_buff *skb)
        return (void *) info->status.status_driver_data;
 }
 
-int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard);
 int mt76x2_mac_start(struct mt76x2_dev *dev);
 void mt76x2_mac_stop(struct mt76x2_dev *dev, bool force);
 void mt76x2_mac_resume(struct mt76x2_dev *dev);
index 73c127f..81c58f8 100644 (file)
@@ -321,6 +321,7 @@ mt76x2_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps)
        struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76);
        int idx = msta->wcid.idx;
 
+       mt76_stop_tx_queues(&dev->mt76, sta, true);
        mt76x2_mac_wcid_set_drop(dev, idx, ps);
 }
 
index fcc37eb..c1c38ca 100644 (file)
@@ -73,16 +73,6 @@ int mt76x2_phy_get_rssi(struct mt76x2_dev *dev, s8 rssi, int chain)
        return rssi;
 }
 
-static u8
-mt76x2_txpower_check(int value)
-{
-       if (value < 0)
-               return 0;
-       if (value > 0x2f)
-               return 0x2f;
-       return value;
-}
-
 static void
 mt76x2_add_rate_power_offset(struct mt76_rate_power *r, int offset)
 {
@@ -102,6 +92,26 @@ mt76x2_limit_rate_power(struct mt76_rate_power *r, int limit)
                        r->all[i] = limit;
 }
 
+static int
+mt76x2_get_min_rate_power(struct mt76_rate_power *r)
+{
+       int i;
+       s8 ret = 0;
+
+       for (i = 0; i < sizeof(r->all); i++) {
+               if (!r->all[i])
+                       continue;
+
+               if (ret)
+                       ret = min(ret, r->all[i]);
+               else
+                       ret = r->all[i];
+       }
+
+       return ret;
+}
+
+
 void mt76x2_phy_set_txpower(struct mt76x2_dev *dev)
 {
        enum nl80211_chan_width width = dev->mt76.chandef.width;
@@ -109,6 +119,7 @@ void mt76x2_phy_set_txpower(struct mt76x2_dev *dev)
        struct mt76x2_tx_power_info txp;
        int txp_0, txp_1, delta = 0;
        struct mt76_rate_power t = {};
+       int base_power, gain;
 
        mt76x2_get_power_info(dev, &txp, chan);
 
@@ -117,26 +128,32 @@ void mt76x2_phy_set_txpower(struct mt76x2_dev *dev)
        else if (width == NL80211_CHAN_WIDTH_80)
                delta = txp.delta_bw80;
 
-       if (txp.target_power > dev->txpower_conf)
-               delta -= txp.target_power - dev->txpower_conf;
-
        mt76x2_get_rate_power(dev, &t, chan);
-       mt76x2_add_rate_power_offset(&t, txp.chain[0].target_power +
-                                  txp.chain[0].delta);
+       mt76x2_add_rate_power_offset(&t, txp.chain[0].target_power);
        mt76x2_limit_rate_power(&t, dev->txpower_conf);
        dev->txpower_cur = mt76x2_get_max_rate_power(&t);
-       mt76x2_add_rate_power_offset(&t, -(txp.chain[0].target_power +
-                                        txp.chain[0].delta + delta));
-       dev->target_power = txp.chain[0].target_power;
-       dev->target_power_delta[0] = txp.chain[0].delta + delta;
-       dev->target_power_delta[1] = txp.chain[1].delta + delta;
-       dev->rate_power = t;
 
-       txp_0 = mt76x2_txpower_check(txp.chain[0].target_power +
-                                  txp.chain[0].delta + delta);
+       base_power = mt76x2_get_min_rate_power(&t);
+       delta += base_power - txp.chain[0].target_power;
+       txp_0 = txp.chain[0].target_power + txp.chain[0].delta + delta;
+       txp_1 = txp.chain[1].target_power + txp.chain[1].delta + delta;
+
+       gain = min(txp_0, txp_1);
+       if (gain < 0) {
+               base_power -= gain;
+               txp_0 -= gain;
+               txp_1 -= gain;
+       } else if (gain > 0x2f) {
+               base_power -= gain - 0x2f;
+               txp_0 = 0x2f;
+               txp_1 = 0x2f;
+       }
 
-       txp_1 = mt76x2_txpower_check(txp.chain[1].target_power +
-                                  txp.chain[1].delta + delta);
+       mt76x2_add_rate_power_offset(&t, -base_power);
+       dev->target_power = txp.chain[0].target_power;
+       dev->target_power_delta[0] = txp_0 - txp.chain[0].target_power;
+       dev->target_power_delta[1] = txp_1 - txp.chain[0].target_power;
+       dev->rate_power = t;
 
        mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_0, txp_0);
        mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_1, txp_1);
@@ -180,7 +197,7 @@ mt76x2_phy_tssi_init_cal(struct mt76x2_dev *dev)
        if (mt76x2_channel_silent(dev))
                return false;
 
-       if (chan->band == NL80211_BAND_2GHZ)
+       if (chan->band == NL80211_BAND_5GHZ)
                flag |= BIT(0);
 
        if (mt76x2_ext_pa_enabled(dev, chan->band))
@@ -257,7 +274,6 @@ mt76x2_phy_set_txpower_regs(struct mt76x2_dev *dev, enum nl80211_band band)
                        mt76_wr(dev, MT_TX_ALC_CFG_2, 0x1b0f0400);
                        mt76_wr(dev, MT_TX_ALC_CFG_3, 0x1b0f0476);
                }
-               mt76_wr(dev, MT_TX_ALC_CFG_4, 0);
 
                if (mt76x2_ext_pa_enabled(dev, band))
                        pa_mode_adj = 0x04000000;
@@ -492,8 +508,10 @@ mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev)
        u8 gain_delta;
        int low_gain;
 
-       dev->cal.avg_rssi[0] = (dev->cal.avg_rssi[0] * 15) / 16 + (rssi0 << 8);
-       dev->cal.avg_rssi[1] = (dev->cal.avg_rssi[1] * 15) / 16 + (rssi1 << 8);
+       dev->cal.avg_rssi[0] = (dev->cal.avg_rssi[0] * 15) / 16 +
+                              (rssi0 << 8) / 16;
+       dev->cal.avg_rssi[1] = (dev->cal.avg_rssi[1] * 15) / 16 +
+                              (rssi1 << 8) / 16;
        dev->cal.avg_rssi_all = (dev->cal.avg_rssi[0] +
                                 dev->cal.avg_rssi[1]) / 512;
 
@@ -661,6 +679,14 @@ int mt76x2_phy_set_channel(struct mt76x2_dev *dev,
        memcpy(dev->cal.agc_gain_cur, dev->cal.agc_gain_init,
               sizeof(dev->cal.agc_gain_cur));
 
+       /* init default values for temp compensation */
+       if (mt76x2_tssi_enabled(dev)) {
+               mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP,
+                              0x38);
+               mt76_rmw_field(dev, MT_TX_ALC_CFG_2, MT_TX_ALC_CFG_2_TEMP_COMP,
+                              0x38);
+       }
+
        ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work,
                                     MT_CALIBRATE_INTERVAL);
 
index 4eef69b..7ecd2d7 100644 (file)
@@ -385,6 +385,10 @@ restart:
                bool empty = false;
                int cur;
 
+               if (test_bit(MT76_SCANNING, &dev->state) ||
+                   test_bit(MT76_RESET, &dev->state))
+                       return -EBUSY;
+
                mtxq = list_first_entry(&hwq->swq, struct mt76_txq, list);
                if (mtxq->send_bar && mtxq->aggr) {
                        struct ieee80211_txq *txq = mtxq_to_txq(mtxq);
@@ -422,12 +426,14 @@ void mt76_txq_schedule(struct mt76_dev *dev, struct mt76_queue *hwq)
 {
        int len;
 
+       rcu_read_lock();
        do {
                if (hwq->swq_queued >= 4 || list_empty(&hwq->swq))
                        break;
 
                len = mt76_txq_schedule_list(dev, hwq);
        } while (len > 0);
+       rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(mt76_txq_schedule);
 
index d55d704..148c36d 100644 (file)
@@ -453,7 +453,7 @@ mt7601u_rx_monitor_beacon(struct mt7601u_dev *dev, struct mt7601u_rxwi *rxwi,
 {
        dev->bcn_freq_off = rxwi->freq_off;
        dev->bcn_phy_mode = FIELD_GET(MT_RXWI_RATE_PHY, rate);
-       dev->avg_rssi = (dev->avg_rssi * 15) / 16 + (rssi << 8);
+       ewma_rssi_add(&dev->avg_rssi, -rssi);
 }
 
 static int
@@ -503,7 +503,7 @@ u32 mt76_mac_process_rx(struct mt7601u_dev *dev, struct sk_buff *skb,
        if (mt7601u_rx_is_our_beacon(dev, data))
                mt7601u_rx_monitor_beacon(dev, rxwi, rate, rssi);
        else if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_U2M))
-               dev->avg_rssi = (dev->avg_rssi * 15) / 16 + (rssi << 8);
+               ewma_rssi_add(&dev->avg_rssi, -rssi);
        spin_unlock_bh(&dev->con_mon_lock);
 
        return len;
index 3c9ea40..7b21016 100644 (file)
@@ -288,6 +288,12 @@ mt7601u_sw_scan_complete(struct ieee80211_hw *hw,
 
        mt7601u_agc_restore(dev);
        clear_bit(MT7601U_STATE_SCANNING, &dev->state);
+
+       ieee80211_queue_delayed_work(dev->hw, &dev->cal_work,
+                                    MT_CALIBRATE_INTERVAL);
+       if (dev->freq_cal.enabled)
+               ieee80211_queue_delayed_work(dev->hw, &dev->freq_cal.work,
+                                            MT_FREQ_CAL_INIT_DELAY);
 }
 
 static int
index 9233744..db317d8 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/completion.h>
 #include <net/mac80211.h>
 #include <linux/debugfs.h>
+#include <linux/average.h>
 
 #include "regs.h"
 
@@ -138,6 +139,8 @@ enum {
        MT7601U_STATE_MORE_STATS,
 };
 
+DECLARE_EWMA(rssi, 10, 4);
+
 /**
  * struct mt7601u_dev - adapter structure
  * @lock:              protects @wcid->tx_rate.
@@ -220,7 +223,7 @@ struct mt7601u_dev {
        s8 bcn_freq_off;
        u8 bcn_phy_mode;
 
-       int avg_rssi; /* starts at 0 and converges */
+       struct ewma_rssi avg_rssi;
 
        u8 agc_save;
 
index ca09a5d..9d2f9a7 100644 (file)
@@ -795,6 +795,7 @@ mt7601u_phy_rf_pa_mode_val(struct mt7601u_dev *dev, int phy_mode, int tx_rate)
        switch (phy_mode) {
        case MT_PHY_TYPE_OFDM:
                tx_rate += 4;
+               /* fall through */
        case MT_PHY_TYPE_CCK:
                reg = dev->rf_pa_mode[0];
                break;
@@ -974,6 +975,7 @@ void mt7601u_agc_restore(struct mt7601u_dev *dev)
 static void mt7601u_agc_tune(struct mt7601u_dev *dev)
 {
        u8 val = mt7601u_agc_default(dev);
+       long avg_rssi;
 
        if (test_bit(MT7601U_STATE_SCANNING, &dev->state))
                return;
@@ -983,9 +985,12 @@ static void mt7601u_agc_tune(struct mt7601u_dev *dev)
         *       Rssi updates are only on beacons and U2M so should work...
         */
        spin_lock_bh(&dev->con_mon_lock);
-       if (dev->avg_rssi <= -70)
+       avg_rssi = ewma_rssi_read(&dev->avg_rssi);
+       WARN_ON_ONCE(avg_rssi == 0);
+       avg_rssi = -avg_rssi;
+       if (avg_rssi <= -70)
                val -= 0x20;
-       else if (dev->avg_rssi <= -60)
+       else if (avg_rssi <= -60)
                val -= 0x10;
        spin_unlock_bh(&dev->con_mon_lock);
 
@@ -1101,7 +1106,7 @@ void mt7601u_phy_con_cal_onoff(struct mt7601u_dev *dev,
        /* Start/stop collecting beacon data */
        spin_lock_bh(&dev->con_mon_lock);
        ether_addr_copy(dev->ap_bssid, info->bssid);
-       dev->avg_rssi = 0;
+       ewma_rssi_init(&dev->avg_rssi);
        dev->bcn_freq_off = MT_FREQ_OFFSET_INVALID;
        spin_unlock_bh(&dev->con_mon_lock);
 
index 0398bec..5122dc7 100644 (file)
@@ -813,6 +813,9 @@ static int qtnf_start_radar_detection(struct wiphy *wiphy,
        struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev);
        int ret;
 
+       if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD))
+               return -ENOTSUPP;
+
        ret = qtnf_cmd_start_cac(vif, chandef, cac_time_ms);
        if (ret)
                pr_err("%s: failed to start CAC ret=%d\n", ndev->name, ret);
@@ -909,6 +912,9 @@ struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus)
 {
        struct wiphy *wiphy;
 
+       if (bus->hw_info.hw_capab & QLINK_HW_CAPAB_DFS_OFFLOAD)
+               qtn_cfg80211_ops.start_radar_detection = NULL;
+
        wiphy = wiphy_new(&qtn_cfg80211_ops, sizeof(struct qtnf_wmac));
        if (!wiphy)
                return NULL;
@@ -982,6 +988,9 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
                        WIPHY_FLAG_AP_UAPSD |
                        WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 
+       if (hw_info->hw_capab & QLINK_HW_CAPAB_DFS_OFFLOAD)
+               wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD);
+
        wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
                                    NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2;
 
index cf26c15..b3bfb4f 100644 (file)
@@ -76,7 +76,7 @@ static int qtnf_netdev_close(struct net_device *ndev)
 
 /* Netdev handler for data transmission.
  */
-static int
+static netdev_tx_t
 qtnf_netdev_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
        struct qtnf_vif *vif;
index bcd415f..16617c4 100644 (file)
@@ -34,12 +34,13 @@ qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif,
 {
        const u8 *sta_addr;
        u16 frame_control;
-       struct station_info sinfo = { 0 };
+       struct station_info *sinfo;
        size_t payload_len;
        u16 tlv_type;
        u16 tlv_value_len;
        size_t tlv_full_len;
        const struct qlink_tlv_hdr *tlv;
+       int ret = 0;
 
        if (unlikely(len < sizeof(*sta_assoc))) {
                pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
@@ -53,6 +54,10 @@ qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif,
                return -EPROTO;
        }
 
+       sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+       if (!sinfo)
+               return -ENOMEM;
+
        sta_addr = sta_assoc->sta_addr;
        frame_control = le16_to_cpu(sta_assoc->frame_control);
 
@@ -61,9 +66,9 @@ qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif,
 
        qtnf_sta_list_add(vif, sta_addr);
 
-       sinfo.assoc_req_ies = NULL;
-       sinfo.assoc_req_ies_len = 0;
-       sinfo.generation = vif->generation;
+       sinfo->assoc_req_ies = NULL;
+       sinfo->assoc_req_ies_len = 0;
+       sinfo->generation = vif->generation;
 
        payload_len = len - sizeof(*sta_assoc);
        tlv = (const struct qlink_tlv_hdr *)sta_assoc->ies;
@@ -73,23 +78,27 @@ qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif,
                tlv_value_len = le16_to_cpu(tlv->len);
                tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr);
 
-               if (tlv_full_len > payload_len)
-                       return -EINVAL;
+               if (tlv_full_len > payload_len) {
+                       ret = -EINVAL;
+                       goto out;
+               }
 
                if (tlv_type == QTN_TLV_ID_IE_SET) {
                        const struct qlink_tlv_ie_set *ie_set;
                        unsigned int ie_len;
 
-                       if (payload_len < sizeof(*ie_set))
-                               return -EINVAL;
+                       if (payload_len < sizeof(*ie_set)) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
 
                        ie_set = (const struct qlink_tlv_ie_set *)tlv;
                        ie_len = tlv_value_len -
                                (sizeof(*ie_set) - sizeof(ie_set->hdr));
 
                        if (ie_set->type == QLINK_IE_SET_ASSOC_REQ && ie_len) {
-                               sinfo.assoc_req_ies = ie_set->ie_data;
-                               sinfo.assoc_req_ies_len = ie_len;
+                               sinfo->assoc_req_ies = ie_set->ie_data;
+                               sinfo->assoc_req_ies_len = ie_len;
                        }
                }
 
@@ -97,13 +106,17 @@ qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif,
                tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len);
        }
 
-       if (payload_len)
-               return -EINVAL;
+       if (payload_len) {
+               ret = -EINVAL;
+               goto out;
+       }
 
-       cfg80211_new_sta(vif->netdev, sta_assoc->sta_addr, &sinfo,
+       cfg80211_new_sta(vif->netdev, sta_assoc->sta_addr, sinfo,
                         GFP_KERNEL);
 
-       return 0;
+out:
+       kfree(sinfo);
+       return ret;
 }
 
 static int
@@ -443,6 +456,17 @@ static int qtnf_event_handle_radar(struct qtnf_vif *vif,
                cfg80211_cac_event(vif->netdev, &chandef,
                                   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
                break;
+       case QLINK_RADAR_CAC_STARTED:
+               if (vif->wdev.cac_started)
+                       break;
+
+               if (!wiphy_ext_feature_isset(wiphy,
+                                            NL80211_EXT_FEATURE_DFS_OFFLOAD))
+                       break;
+
+               cfg80211_cac_event(vif->netdev, &chandef,
+                                  NL80211_RADAR_CAC_STARTED, GFP_KERNEL);
+               break;
        default:
                pr_warn("%s: unhandled radar event %u\n",
                        vif->netdev->name, ev->event);
index f117904..6c1e139 100644 (file)
@@ -1185,6 +1185,10 @@ static void qtnf_fw_work_handler(struct work_struct *work)
        if (qtnf_poll_state(&priv->bda->bda_ep_state, QTN_EP_FW_LOADRDY,
                            QTN_FW_DL_TIMEOUT_MS)) {
                pr_err("card is not ready\n");
+
+               if (!flashboot)
+                       release_firmware(fw);
+
                goto fw_load_fail;
        }
 
index 9bf3ae4..9ab27e1 100644 (file)
@@ -68,10 +68,12 @@ struct qlink_msg_header {
  * @QLINK_HW_CAPAB_STA_INACT_TIMEOUT: device implements a logic to kick-out
  *     associated STAs due to inactivity. Inactivity timeout period is taken
  *     from QLINK_CMD_START_AP parameters.
+ * @QLINK_HW_CAPAB_DFS_OFFLOAD: device implements DFS offload functionality
  */
 enum qlink_hw_capab {
-       QLINK_HW_CAPAB_REG_UPDATE = BIT(0),
-       QLINK_HW_CAPAB_STA_INACT_TIMEOUT = BIT(1),
+       QLINK_HW_CAPAB_REG_UPDATE               = BIT(0),
+       QLINK_HW_CAPAB_STA_INACT_TIMEOUT        = BIT(1),
+       QLINK_HW_CAPAB_DFS_OFFLOAD              = BIT(2),
 };
 
 enum qlink_iface_type {
@@ -1031,6 +1033,7 @@ enum qlink_radar_event {
        QLINK_RADAR_CAC_ABORTED,
        QLINK_RADAR_NOP_FINISHED,
        QLINK_RADAR_PRE_CAC_EXPIRED,
+       QLINK_RADAR_CAC_STARTED,
 };
 
 /**
index 6a8c93f..b05ed2f 100644 (file)
@@ -94,6 +94,7 @@
 #define REV_RT3390E                    0x0211
 #define REV_RT3593E                    0x0211
 #define REV_RT5390F                    0x0502
+#define REV_RT5370G                    0x0503
 #define REV_RT5390R                    0x1502
 #define REV_RT5592C                    0x0221
 
 #define TX_PWR_CFG_3_MCS13             FIELD32(0x000000f0)
 #define TX_PWR_CFG_3_MCS14             FIELD32(0x00000f00)
 #define TX_PWR_CFG_3_MCS15             FIELD32(0x0000f000)
-#define TX_PWR_CFG_3_UKNOWN1           FIELD32(0x000f0000)
-#define TX_PWR_CFG_3_UKNOWN2           FIELD32(0x00f00000)
-#define TX_PWR_CFG_3_UKNOWN3           FIELD32(0x0f000000)
-#define TX_PWR_CFG_3_UKNOWN4           FIELD32(0xf0000000)
+#define TX_PWR_CFG_3_UNKNOWN1          FIELD32(0x000f0000)
+#define TX_PWR_CFG_3_UNKNOWN2          FIELD32(0x00f00000)
+#define TX_PWR_CFG_3_UNKNOWN3          FIELD32(0x0f000000)
+#define TX_PWR_CFG_3_UNKNOWN4          FIELD32(0xf0000000)
 /* bits for 3T devices */
 #define TX_PWR_CFG_3_MCS12_CH0         FIELD32(0x0000000f)
 #define TX_PWR_CFG_3_MCS12_CH1         FIELD32(0x000000f0)
  * TX_PWR_CFG_4:
  */
 #define TX_PWR_CFG_4                   0x1324
-#define TX_PWR_CFG_4_UKNOWN5           FIELD32(0x0000000f)
-#define TX_PWR_CFG_4_UKNOWN6           FIELD32(0x000000f0)
-#define TX_PWR_CFG_4_UKNOWN7           FIELD32(0x00000f00)
-#define TX_PWR_CFG_4_UKNOWN8           FIELD32(0x0000f000)
+#define TX_PWR_CFG_4_UNKNOWN5          FIELD32(0x0000000f)
+#define TX_PWR_CFG_4_UNKNOWN6          FIELD32(0x000000f0)
+#define TX_PWR_CFG_4_UNKNOWN7          FIELD32(0x00000f00)
+#define TX_PWR_CFG_4_UNKNOWN8          FIELD32(0x0000f000)
 /* bits for 3T devices */
 #define TX_PWR_CFG_4_STBC4_CH0         FIELD32(0x0000000f)
 #define TX_PWR_CFG_4_STBC4_CH1         FIELD32(0x000000f0)
index 429d07b..a567bc2 100644 (file)
@@ -1557,12 +1557,13 @@ static void rt2800_set_max_psdu_len(struct rt2x00_dev *rt2x00dev)
        rt2800_register_write(rt2x00dev, MAX_LEN_CFG, reg);
 }
 
-int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif,
+int rt2800_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                   struct ieee80211_sta *sta)
 {
-       int wcid;
-       struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta);
+       struct rt2x00_dev *rt2x00dev = hw->priv;
        struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+       struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta);
+       int wcid;
 
        /*
         * Limit global maximum TX AMPDU length to smallest value of all
@@ -1608,8 +1609,10 @@ int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif,
 }
 EXPORT_SYMBOL_GPL(rt2800_sta_add);
 
-int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, struct ieee80211_sta *sta)
+int rt2800_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                     struct ieee80211_sta *sta)
 {
+       struct rt2x00_dev *rt2x00dev = hw->priv;
        struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
        struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta);
        int wcid = sta_priv->wcid;
@@ -6220,8 +6223,9 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
                rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
        }
 
-       /* This chip has hardware antenna diversity*/
-       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) {
+       /* These chips have hardware RX antenna diversity */
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R) ||
+           rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5370G)) {
                rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */
                rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */
                rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */
@@ -8748,7 +8752,9 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
                rt2x00dev->default_ant.rx = ANTENNA_A;
        }
 
-       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) {
+       /* These chips have hardware RX antenna diversity */
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R) ||
+           rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5370G)) {
                rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; /* Unused */
                rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; /* Unused */
        }
index 275e396..51d9c2a 100644 (file)
@@ -208,9 +208,10 @@ int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev,
 int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
                               struct rt2x00lib_crypto *crypto,
                               struct ieee80211_key_conf *key);
-int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif,
+int rt2800_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                   struct ieee80211_sta *sta);
-int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, struct ieee80211_sta *sta);
+int rt2800_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                     struct ieee80211_sta *sta);
 void rt2800_config_filter(struct rt2x00_dev *rt2x00dev,
                          const unsigned int filter_flags);
 void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
index 1123e2b..e1a7ed7 100644 (file)
@@ -600,6 +600,7 @@ void rt2800mmio_kick_queue(struct data_queue *queue)
        case QID_AC_VI:
        case QID_AC_BE:
        case QID_AC_BK:
+               WARN_ON_ONCE(rt2x00queue_empty(queue));
                entry = rt2x00queue_get_entry(queue, Q_INDEX);
                rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid),
                                          entry->entry_idx);
index 1172eef..71b1aff 100644 (file)
@@ -311,8 +311,8 @@ static const struct ieee80211_ops rt2800pci_mac80211_ops = {
        .get_stats              = rt2x00mac_get_stats,
        .get_key_seq            = rt2800_get_key_seq,
        .set_rts_threshold      = rt2800_set_rts_threshold,
-       .sta_add                = rt2x00mac_sta_add,
-       .sta_remove             = rt2x00mac_sta_remove,
+       .sta_add                = rt2800_sta_add,
+       .sta_remove             = rt2800_sta_remove,
        .bss_info_changed       = rt2x00mac_bss_info_changed,
        .conf_tx                = rt2800_conf_tx,
        .get_tsf                = rt2800_get_tsf,
@@ -377,8 +377,6 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
        .config_erp             = rt2800_config_erp,
        .config_ant             = rt2800_config_ant,
        .config                 = rt2800_config,
-       .sta_add                = rt2800_sta_add,
-       .sta_remove             = rt2800_sta_remove,
 };
 
 static const struct rt2x00_ops rt2800pci_ops = {
index 6848ebc..a502816 100644 (file)
@@ -150,8 +150,8 @@ static const struct ieee80211_ops rt2800soc_mac80211_ops = {
        .get_stats              = rt2x00mac_get_stats,
        .get_key_seq            = rt2800_get_key_seq,
        .set_rts_threshold      = rt2800_set_rts_threshold,
-       .sta_add                = rt2x00mac_sta_add,
-       .sta_remove             = rt2x00mac_sta_remove,
+       .sta_add                = rt2800_sta_add,
+       .sta_remove             = rt2800_sta_remove,
        .bss_info_changed       = rt2x00mac_bss_info_changed,
        .conf_tx                = rt2800_conf_tx,
        .get_tsf                = rt2800_get_tsf,
@@ -216,8 +216,6 @@ static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = {
        .config_erp             = rt2800_config_erp,
        .config_ant             = rt2800_config_ant,
        .config                 = rt2800_config,
-       .sta_add                = rt2800_sta_add,
-       .sta_remove             = rt2800_sta_remove,
 };
 
 static const struct rt2x00_ops rt2800soc_ops = {
index d901a41..98a7313 100644 (file)
@@ -797,8 +797,8 @@ static const struct ieee80211_ops rt2800usb_mac80211_ops = {
        .get_stats              = rt2x00mac_get_stats,
        .get_key_seq            = rt2800_get_key_seq,
        .set_rts_threshold      = rt2800_set_rts_threshold,
-       .sta_add                = rt2x00mac_sta_add,
-       .sta_remove             = rt2x00mac_sta_remove,
+       .sta_add                = rt2800_sta_add,
+       .sta_remove             = rt2800_sta_remove,
        .bss_info_changed       = rt2x00mac_bss_info_changed,
        .conf_tx                = rt2800_conf_tx,
        .get_tsf                = rt2800_get_tsf,
@@ -858,8 +858,6 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
        .config_erp             = rt2800_config_erp,
        .config_ant             = rt2800_config_ant,
        .config                 = rt2800_config,
-       .sta_add                = rt2800_sta_add,
-       .sta_remove             = rt2800_sta_remove,
 };
 
 static void rt2800usb_queue_init(struct data_queue *queue)
index 1f38c33..a279a43 100644 (file)
@@ -1457,10 +1457,6 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 #else
 #define rt2x00mac_set_key      NULL
 #endif /* CONFIG_RT2X00_LIB_CRYPTO */
-int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                     struct ieee80211_sta *sta);
-int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                        struct ieee80211_sta *sta);
 void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
                             const u8 *mac_addr);
index a971bc7..c380c1f 100644 (file)
@@ -739,8 +739,7 @@ void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                return;
 
        tx_queue_for_each(rt2x00dev, queue)
-               if (!rt2x00queue_empty(queue))
-                       rt2x00queue_flush_queue(queue, drop);
+               rt2x00queue_flush_queue(queue, drop);
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_flush);
 
index a6884e7..7c1f8f5 100644 (file)
@@ -1000,6 +1000,8 @@ void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)
                (queue->qid == QID_AC_BE) ||
                (queue->qid == QID_AC_BK);
 
+       if (rt2x00queue_empty(queue))
+               return;
 
        /*
         * If we are not supposed to drop any pending
index 8fce371..f22fec0 100644 (file)
@@ -1783,7 +1783,7 @@ static void btc8192e2ant_tdma_duration_adjust(struct btc_coexist *btcoexist,
                bool scan = false, link = false, roam = false;
 
                RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD,
-                        "[BTCoex], PsTdma type dismatch!!!, ");
+                        "[BTCoex], PsTdma type mismatch!!!, ");
                RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD,
                         "curPsTdma=%d, recordPsTdma=%d\n",
                         coex_dm->cur_ps_tdma, coex_dm->tdma_adj_type);
index 73ec319..279fe01 100644 (file)
@@ -2766,7 +2766,7 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist,
        if (coex_dm->cur_ps_tdma != coex_dm->ps_tdma_du_adj_type) {
                bool scan = false, link = false, roam = false;
                RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD,
-                        "[BTCoex], PsTdma type dismatch!!!, curPsTdma=%d, recordPsTdma=%d\n",
+                        "[BTCoex], PsTdma type mismatch!!!, curPsTdma=%d, recordPsTdma=%d\n",
                         coex_dm->cur_ps_tdma, coex_dm->ps_tdma_du_adj_type);
 
                btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan);
index 202597c..b5d6587 100644 (file)
@@ -1584,11 +1584,7 @@ static void btc8821a1ant_act_bt_sco_hid_only_busy(struct btc_coexist *btcoexist,
        /* tdma and coex table */
        btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5);
 
-       if (BT_8821A_1ANT_WIFI_STATUS_NON_CONNECTED_ASSO_AUTH_SCAN ==
-           wifi_status)
-               btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1);
-       else
-               btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1);
+       btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1);
 }
 
 static void btc8821a1ant_act_wifi_con_bt_acl_busy(struct btc_coexist *btcoexist,
@@ -1991,16 +1987,9 @@ static void btc8821a1ant_run_coexist_mechanism(struct btc_coexist *btcoexist)
                        wifi_rssi_state =
                                btc8821a1ant_wifi_rssi_state(btcoexist, 1, 2,
                                                             30, 0);
-                       if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) ||
-                           (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) {
-                               btc8821a1ant_limited_tx(btcoexist,
-                                                       NORMAL_EXEC, 1, 1,
-                                                       0, 1);
-                       } else {
-                               btc8821a1ant_limited_tx(btcoexist,
-                                                       NORMAL_EXEC, 1, 1,
-                                                       0, 1);
-                       }
+                       btc8821a1ant_limited_tx(btcoexist,
+                                               NORMAL_EXEC, 1, 1,
+                                               0, 1);
                } else {
                        btc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC,
                                                0, 0, 0, 0);
index 2202d5e..01a9d30 100644 (file)
@@ -2614,7 +2614,7 @@ static void btc8821a2ant_tdma_duration_adjust(struct btc_coexist *btcoexist,
                bool scan = false, link = false, roam = false;
 
                RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD,
-                        "[BTCoex], PsTdma type dismatch!!!, cur_ps_tdma = %d, recordPsTdma = %d\n",
+                        "[BTCoex], PsTdma type mismatch!!!, cur_ps_tdma = %d, recordPsTdma = %d\n",
                         coex_dm->cur_ps_tdma, coex_dm->ps_tdma_du_adj_type);
 
                btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan);
index fd7928f..3f2295f 100644 (file)
@@ -2574,11 +2574,11 @@ void rtl92ee_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw,
                        rtlpriv->btcoexist.btc_info.btcoexist = 0;
 
                rtlpriv->btcoexist.btc_info.bt_type = BT_RTL8192E;
-               rtlpriv->btcoexist.btc_info.ant_num = ANT_TOTAL_X2;
+               rtlpriv->btcoexist.btc_info.ant_num = ANT_X2;
        } else {
                rtlpriv->btcoexist.btc_info.btcoexist = 1;
                rtlpriv->btcoexist.btc_info.bt_type = BT_RTL8192E;
-               rtlpriv->btcoexist.btc_info.ant_num = ANT_TOTAL_X1;
+               rtlpriv->btcoexist.btc_info.ant_num = ANT_X1;
        }
 }
 
index ce17540..208010f 100644 (file)
@@ -2842,11 +2842,6 @@ enum bt_co_type {
        BT_RTL8812A = 11,
 };
 
-enum bt_total_ant_num {
-       ANT_TOTAL_X2 = 0,
-       ANT_TOTAL_X1 = 1
-};
-
 enum bt_cur_state {
        BT_OFF = 0,
        BT_ON = 1,
index d055099..c8ba148 100644 (file)
@@ -73,6 +73,7 @@ int rsi_coex_recv_pkt(struct rsi_common *common, u8 *msg)
        switch (msg_type) {
        case COMMON_CARD_READY_IND:
                rsi_dbg(INFO_ZONE, "common card ready received\n");
+               common->hibernate_resume = false;
                rsi_handle_card_ready(common, msg);
                break;
        case SLEEP_NOTIFY_IND:
index 5dafd2e..3644d7d 100644 (file)
@@ -411,11 +411,30 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
        if ((ieee80211_is_mgmt(wh->frame_control)) ||
            (ieee80211_is_ctl(wh->frame_control)) ||
            (ieee80211_is_qos_nullfunc(wh->frame_control))) {
+               if (ieee80211_is_assoc_req(wh->frame_control) ||
+                   ieee80211_is_reassoc_req(wh->frame_control)) {
+                       struct ieee80211_bss_conf *bss = &vif->bss_conf;
+
+                       common->eapol4_confirm = false;
+                       rsi_hal_send_sta_notify_frame(common,
+                                                     RSI_IFTYPE_STATION,
+                                                     STA_CONNECTED, bss->bssid,
+                                                     bss->qos, bss->aid, 0,
+                                                     vif);
+               }
+
                q_num = MGMT_SOFT_Q;
                skb->priority = q_num;
+
+               if (rsi_prepare_mgmt_desc(common, skb)) {
+                       rsi_dbg(ERR_ZONE, "Failed to prepare desc\n");
+                       goto xmit_fail;
+               }
        } else {
                if (ieee80211_is_data_qos(wh->frame_control)) {
-                       tid = (skb->data[24] & IEEE80211_QOS_TID);
+                       u8 *qos = ieee80211_get_qos_ctl(wh);
+
+                       tid = *qos & IEEE80211_QOS_CTL_TID_MASK;
                        skb->priority = TID_TO_WME_AC(tid);
                } else {
                        tid = IEEE80211_NONQOS_TID;
@@ -433,6 +452,8 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
                        if (!rsta)
                                goto xmit_fail;
                        tx_params->sta_id = rsta->sta_id;
+               } else {
+                       tx_params->sta_id = 0;
                }
 
                if (rsta) {
@@ -443,6 +464,14 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
                                                              tid, 0);
                        }
                }
+               if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
+                       q_num = MGMT_SOFT_Q;
+                       skb->priority = q_num;
+               }
+               if (rsi_prepare_data_desc(common, skb)) {
+                       rsi_dbg(ERR_ZONE, "Failed to prepare data desc\n");
+                       goto xmit_fail;
+               }
        }
 
        if ((q_num < MGMT_SOFT_Q) &&
@@ -456,7 +485,7 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
        }
 
        rsi_core_queue_pkt(common, skb);
-       rsi_dbg(DATA_TX_ZONE, "%s: ===> Scheduling TX thead <===\n", __func__);
+       rsi_dbg(DATA_TX_ZONE, "%s: ===> Scheduling TX thread <===\n", __func__);
        rsi_set_event(&common->tx_thread.event);
 
        return;
index de608ae..0761e61 100644 (file)
@@ -45,7 +45,7 @@ int rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb)
        return status;
 }
 
-static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb)
+int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb)
 {
        struct rsi_hw *adapter = common->priv;
        struct ieee80211_hdr *wh = NULL;
@@ -55,7 +55,7 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb)
        struct rsi_mgmt_desc *mgmt_desc;
        struct skb_info *tx_params;
        struct ieee80211_bss_conf *bss = NULL;
-       struct xtended_desc *xtend_desc = NULL;
+       struct rsi_xtended_desc *xtend_desc = NULL;
        u8 header_size;
        u32 dword_align_bytes = 0;
 
@@ -69,7 +69,7 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb)
        vif = tx_params->vif;
 
        /* Update header size */
-       header_size = FRAME_DESC_SZ + sizeof(struct xtended_desc);
+       header_size = FRAME_DESC_SZ + sizeof(struct rsi_xtended_desc);
        if (header_size > skb_headroom(skb)) {
                rsi_dbg(ERR_ZONE,
                        "%s: Failed to add extended descriptor\n",
@@ -92,7 +92,7 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb)
        wh = (struct ieee80211_hdr *)&skb->data[header_size];
 
        mgmt_desc = (struct rsi_mgmt_desc *)skb->data;
-       xtend_desc = (struct xtended_desc *)&skb->data[FRAME_DESC_SZ];
+       xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ];
 
        rsi_set_len_qno(&mgmt_desc->len_qno, (skb->len - FRAME_DESC_SZ),
                        RSI_WIFI_MGMT_Q);
@@ -113,17 +113,6 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb)
        if (conf_is_ht40(conf))
                mgmt_desc->bbp_info = cpu_to_le16(FULL40M_ENABLE);
 
-       if (ieee80211_is_probe_req(wh->frame_control)) {
-               if (!bss->assoc) {
-                       rsi_dbg(INFO_ZONE,
-                               "%s: blocking mgmt queue\n", __func__);
-                       mgmt_desc->misc_flags = RSI_DESC_REQUIRE_CFM_TO_HOST;
-                       xtend_desc->confirm_frame_type = PROBEREQ_CONFIRM;
-                       common->mgmt_q_block = true;
-                       rsi_dbg(INFO_ZONE, "Mgmt queue blocked\n");
-               }
-       }
-
        if (ieee80211_is_probe_resp(wh->frame_control)) {
                mgmt_desc->misc_flags |= (RSI_ADD_DELTA_TSF_VAP_ID |
                                          RSI_FETCH_RETRY_CNT_FRM_HST);
@@ -149,7 +138,7 @@ static int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb)
 }
 
 /* This function prepares descriptor for given data packet */
-static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb)
+int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb)
 {
        struct rsi_hw *adapter = common->priv;
        struct ieee80211_vif *vif;
@@ -158,7 +147,7 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb)
        struct skb_info *tx_params;
        struct ieee80211_bss_conf *bss;
        struct rsi_data_desc *data_desc;
-       struct xtended_desc *xtend_desc;
+       struct rsi_xtended_desc *xtend_desc;
        u8 ieee80211_size = MIN_802_11_HDR_LEN;
        u8 header_size;
        u8 vap_id = 0;
@@ -170,7 +159,7 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb)
        bss = &vif->bss_conf;
        tx_params = (struct skb_info *)info->driver_data;
 
-       header_size = FRAME_DESC_SZ + sizeof(struct xtended_desc);
+       header_size = FRAME_DESC_SZ + sizeof(struct rsi_xtended_desc);
        if (header_size > skb_headroom(skb)) {
                rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__);
                return -ENOSPC;
@@ -188,7 +177,7 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb)
        data_desc = (struct rsi_data_desc *)skb->data;
        memset(data_desc, 0, header_size);
 
-       xtend_desc = (struct xtended_desc *)&skb->data[FRAME_DESC_SZ];
+       xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ];
        wh = (struct ieee80211_hdr *)&skb->data[header_size];
        seq_num = IEEE80211_SEQ_TO_SN(le16_to_cpu(wh->seq_ctrl));
 
@@ -243,6 +232,18 @@ static int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb)
                data_desc->misc_flags |= RSI_FETCH_RETRY_CNT_FRM_HST;
 #define EAPOL_RETRY_CNT 15
                xtend_desc->retry_cnt = EAPOL_RETRY_CNT;
+
+               if (common->eapol4_confirm)
+                       skb->priority = VO_Q;
+               else
+                       rsi_set_len_qno(&data_desc->len_qno,
+                                       (skb->len - FRAME_DESC_SZ),
+                                       RSI_WIFI_MGMT_Q);
+               if ((skb->len - header_size) == EAPOL4_PACKET_LEN) {
+                       data_desc->misc_flags |=
+                               RSI_DESC_REQUIRE_CFM_TO_HOST;
+                       xtend_desc->confirm_frame_type = EAPOL4_CONFIRM;
+               }
        }
 
        data_desc->mac_flags = cpu_to_le16(seq_num & 0xfff);
@@ -282,8 +283,11 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb)
        struct rsi_hw *adapter = common->priv;
        struct ieee80211_vif *vif;
        struct ieee80211_tx_info *info;
+       struct skb_info *tx_params;
        struct ieee80211_bss_conf *bss;
+       struct ieee80211_hdr *wh;
        int status = -EINVAL;
+       u8 header_size;
 
        if (!skb)
                return 0;
@@ -295,16 +299,15 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb)
                goto err;
        vif = info->control.vif;
        bss = &vif->bss_conf;
+       tx_params = (struct skb_info *)info->driver_data;
+       header_size = tx_params->internal_hdr_size;
+       wh = (struct ieee80211_hdr *)&skb->data[header_size];
 
        if (((vif->type == NL80211_IFTYPE_STATION) ||
             (vif->type == NL80211_IFTYPE_P2P_CLIENT)) &&
            (!bss->assoc))
                goto err;
 
-       status = rsi_prepare_data_desc(common, skb);
-       if (status)
-               goto err;
-
        status = rsi_send_pkt_to_bus(common, skb);
        if (status)
                rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", __func__);
@@ -327,12 +330,18 @@ int rsi_send_mgmt_pkt(struct rsi_common *common,
                      struct sk_buff *skb)
 {
        struct rsi_hw *adapter = common->priv;
+       struct ieee80211_bss_conf *bss;
+       struct ieee80211_hdr *wh;
        struct ieee80211_tx_info *info;
        struct skb_info *tx_params;
+       struct rsi_mgmt_desc *mgmt_desc;
+       struct rsi_xtended_desc *xtend_desc;
        int status = -E2BIG;
+       u8 header_size;
 
        info = IEEE80211_SKB_CB(skb);
        tx_params = (struct skb_info *)info->driver_data;
+       header_size = tx_params->internal_hdr_size;
 
        if (tx_params->flags & INTERNAL_MGMT_PKT) {
                status = adapter->host_intf_ops->write_pkt(common->priv,
@@ -346,15 +355,25 @@ int rsi_send_mgmt_pkt(struct rsi_common *common,
                return status;
        }
 
-       if (FRAME_DESC_SZ > skb_headroom(skb))
-               goto err;
+       bss = &info->control.vif->bss_conf;
+       wh = (struct ieee80211_hdr *)&skb->data[header_size];
+       mgmt_desc = (struct rsi_mgmt_desc *)skb->data;
+       xtend_desc = (struct rsi_xtended_desc *)&skb->data[FRAME_DESC_SZ];
+
+       /* Indicate to firmware to give cfm for probe */
+       if (ieee80211_is_probe_req(wh->frame_control) && !bss->assoc) {
+               rsi_dbg(INFO_ZONE,
+                       "%s: blocking mgmt queue\n", __func__);
+               mgmt_desc->misc_flags = RSI_DESC_REQUIRE_CFM_TO_HOST;
+               xtend_desc->confirm_frame_type = PROBEREQ_CONFIRM;
+               common->mgmt_q_block = true;
+               rsi_dbg(INFO_ZONE, "Mgmt queue blocked\n");
+       }
 
-       rsi_prepare_mgmt_desc(common, skb);
        status = rsi_send_pkt_to_bus(common, skb);
        if (status)
                rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__);
 
-err:
        rsi_indicate_tx_status(common->priv, skb, status);
        return status;
 }
@@ -616,28 +635,32 @@ static int bl_write_header(struct rsi_hw *adapter, u8 *flash_content,
                           u32 content_size)
 {
        struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops;
-       struct bl_header bl_hdr;
+       struct bl_header *bl_hdr;
        u32 write_addr, write_len;
        int status;
 
-       bl_hdr.flags = 0;
-       bl_hdr.image_no = cpu_to_le32(adapter->priv->coex_mode);
-       bl_hdr.check_sum = cpu_to_le32(
-                               *(u32 *)&flash_content[CHECK_SUM_OFFSET]);
-       bl_hdr.flash_start_address = cpu_to_le32(
-                                       *(u32 *)&flash_content[ADDR_OFFSET]);
-       bl_hdr.flash_len = cpu_to_le32(*(u32 *)&flash_content[LEN_OFFSET]);
+       bl_hdr = kzalloc(sizeof(*bl_hdr), GFP_KERNEL);
+       if (!bl_hdr)
+               return -ENOMEM;
+
+       bl_hdr->flags = 0;
+       bl_hdr->image_no = cpu_to_le32(adapter->priv->coex_mode);
+       bl_hdr->check_sum =
+               cpu_to_le32(*(u32 *)&flash_content[CHECK_SUM_OFFSET]);
+       bl_hdr->flash_start_address =
+               cpu_to_le32(*(u32 *)&flash_content[ADDR_OFFSET]);
+       bl_hdr->flash_len = cpu_to_le32(*(u32 *)&flash_content[LEN_OFFSET]);
        write_len = sizeof(struct bl_header);
 
        if (adapter->rsi_host_intf == RSI_HOST_INTF_USB) {
                write_addr = PING_BUFFER_ADDRESS;
                status = hif_ops->write_reg_multiple(adapter, write_addr,
-                                                (u8 *)&bl_hdr, write_len);
+                                                (u8 *)bl_hdr, write_len);
                if (status < 0) {
                        rsi_dbg(ERR_ZONE,
                                "%s: Failed to load Version/CRC structure\n",
                                __func__);
-                       return status;
+                       goto fail;
                }
        } else {
                write_addr = PING_BUFFER_ADDRESS >> 16;
@@ -646,20 +669,23 @@ static int bl_write_header(struct rsi_hw *adapter, u8 *flash_content,
                        rsi_dbg(ERR_ZONE,
                                "%s: Unable to set ms word to common reg\n",
                                __func__);
-                       return status;
+                       goto fail;
                }
                write_addr = RSI_SD_REQUEST_MASTER |
                             (PING_BUFFER_ADDRESS & 0xFFFF);
                status = hif_ops->write_reg_multiple(adapter, write_addr,
-                                                (u8 *)&bl_hdr, write_len);
+                                                (u8 *)bl_hdr, write_len);
                if (status < 0) {
                        rsi_dbg(ERR_ZONE,
                                "%s: Failed to load Version/CRC structure\n",
                                __func__);
-                       return status;
+                       goto fail;
                }
        }
-       return 0;
+       status = 0;
+fail:
+       kfree(bl_hdr);
+       return status;
 }
 
 static u32 read_flash_capacity(struct rsi_hw *adapter)
index 32f5cb4..3faa044 100644 (file)
@@ -614,7 +614,7 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw,
 
        /* Power save parameters */
        if (changed & IEEE80211_CONF_CHANGE_PS) {
-               struct ieee80211_vif *vif;
+               struct ieee80211_vif *vif, *sta_vif = NULL;
                unsigned long flags;
                int i, set_ps = 1;
 
@@ -628,13 +628,17 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw,
                                set_ps = 0;
                                break;
                        }
+                       if ((vif->type == NL80211_IFTYPE_STATION ||
+                            vif->type == NL80211_IFTYPE_P2P_CLIENT) &&
+                           (!sta_vif || vif->bss_conf.assoc))
+                               sta_vif = vif;
                }
-               if (set_ps) {
+               if (set_ps && sta_vif) {
                        spin_lock_irqsave(&adapter->ps_lock, flags);
                        if (conf->flags & IEEE80211_CONF_PS)
-                               rsi_enable_ps(adapter, vif);
+                               rsi_enable_ps(adapter, sta_vif);
                        else
-                               rsi_disable_ps(adapter, vif);
+                               rsi_disable_ps(adapter, sta_vif);
                        spin_unlock_irqrestore(&adapter->ps_lock, flags);
                }
        }
@@ -737,7 +741,8 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw,
                                      bss_conf->bssid,
                                      bss_conf->qos,
                                      bss_conf->aid,
-                                     NULL, 0, vif);
+                                     NULL, 0,
+                                     bss_conf->assoc_capability, vif);
                adapter->ps_info.dtim_interval_duration = bss->dtim_period;
                adapter->ps_info.listen_interval = conf->listen_interval;
 
@@ -906,14 +911,25 @@ static int rsi_hal_key_config(struct ieee80211_hw *hw,
                }
        }
 
-       return rsi_hal_load_key(adapter->priv,
-                               key->key,
-                               key->keylen,
-                               key_type,
-                               key->keyidx,
-                               key->cipher,
-                               sta_id,
-                               vif);
+       status = rsi_hal_load_key(adapter->priv,
+                                 key->key,
+                                 key->keylen,
+                                 key_type,
+                                 key->keyidx,
+                                 key->cipher,
+                                 sta_id,
+                                 vif);
+       if (status)
+               return status;
+
+       if (vif->type == NL80211_IFTYPE_STATION && key->key &&
+           (key->cipher == WLAN_CIPHER_SUITE_WEP104 ||
+            key->cipher == WLAN_CIPHER_SUITE_WEP40)) {
+               if (!rsi_send_block_unblock_frame(adapter->priv, false))
+                       adapter->priv->hw_data_qs_blocked = false;
+       }
+
+       return 0;
 }
 
 /**
@@ -1391,7 +1407,7 @@ static int rsi_mac80211_sta_add(struct ieee80211_hw *hw,
                        rsi_dbg(INFO_ZONE, "Indicate bss status to device\n");
                        rsi_inform_bss_status(common, RSI_OPMODE_AP, 1,
                                              sta->addr, sta->wme, sta->aid,
-                                             sta, sta_idx, vif);
+                                             sta, sta_idx, 0, vif);
 
                        if (common->key) {
                                struct ieee80211_key_conf *key = common->key;
@@ -1469,7 +1485,7 @@ static int rsi_mac80211_sta_remove(struct ieee80211_hw *hw,
                                rsi_inform_bss_status(common, RSI_OPMODE_AP, 0,
                                                      sta->addr, sta->wme,
                                                      sta->aid, sta, sta_idx,
-                                                     vif);
+                                                     0, vif);
                                rsta->sta = NULL;
                                rsta->sta_id = -1;
                                for (cnt = 0; cnt < IEEE80211_NUM_TIDS; cnt++)
@@ -1788,15 +1804,21 @@ int rsi_config_wowlan(struct rsi_hw *adapter, struct cfg80211_wowlan *wowlan)
        struct rsi_common *common = adapter->priv;
        u16 triggers = 0;
        u16 rx_filter_word = 0;
-       struct ieee80211_bss_conf *bss = &adapter->vifs[0]->bss_conf;
+       struct ieee80211_bss_conf *bss = NULL;
 
        rsi_dbg(INFO_ZONE, "Config WoWLAN to device\n");
 
+       if (!adapter->vifs[0])
+               return -EINVAL;
+
+       bss = &adapter->vifs[0]->bss_conf;
+
        if (WARN_ON(!wowlan)) {
                rsi_dbg(ERR_ZONE, "WoW triggers not enabled\n");
                return -EINVAL;
        }
 
+       common->wow_flags |= RSI_WOW_ENABLED;
        triggers = rsi_wow_map_triggers(common, wowlan);
        if (!triggers) {
                rsi_dbg(ERR_ZONE, "%s:No valid WoW triggers\n", __func__);
@@ -1819,7 +1841,6 @@ int rsi_config_wowlan(struct rsi_hw *adapter, struct cfg80211_wowlan *wowlan)
 
        rx_filter_word = (ALLOW_DATA_ASSOC_PEER | DISALLOW_BEACONS);
        rsi_send_rx_filter_frame(common, rx_filter_word);
-       common->wow_flags |= RSI_WOW_ENABLED;
 
        return 0;
 }
@@ -1939,9 +1960,8 @@ int rsi_mac80211_attach(struct rsi_common *common)
        hw->uapsd_queues = RSI_IEEE80211_UAPSD_QUEUES;
        hw->uapsd_max_sp_len = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL;
 
-       hw->max_tx_aggregation_subframes = 6;
-       rsi_register_rates_channels(adapter, NL80211_BAND_2GHZ);
-       rsi_register_rates_channels(adapter, NL80211_BAND_5GHZ);
+       hw->max_tx_aggregation_subframes = RSI_MAX_TX_AGGR_FRMS;
+       hw->max_rx_aggregation_subframes = RSI_MAX_RX_AGGR_FRMS;
        hw->rate_control_algorithm = "AARF";
 
        SET_IEEE80211_PERM_ADDR(hw, common->mac_addr);
@@ -1962,10 +1982,15 @@ int rsi_mac80211_attach(struct rsi_common *common)
 
        wiphy->available_antennas_rx = 1;
        wiphy->available_antennas_tx = 1;
+
+       rsi_register_rates_channels(adapter, NL80211_BAND_2GHZ);
        wiphy->bands[NL80211_BAND_2GHZ] =
                &adapter->sbands[NL80211_BAND_2GHZ];
-       wiphy->bands[NL80211_BAND_5GHZ] =
-               &adapter->sbands[NL80211_BAND_5GHZ];
+       if (common->num_supp_bands > 1) {
+               rsi_register_rates_channels(adapter, NL80211_BAND_5GHZ);
+               wiphy->bands[NL80211_BAND_5GHZ] =
+                       &adapter->sbands[NL80211_BAND_5GHZ];
+       }
 
        /* AP Parameters */
        wiphy->max_ap_assoc_sta = rsi_max_ap_stas[common->oper_mode - 1];
@@ -1991,6 +2016,9 @@ int rsi_mac80211_attach(struct rsi_common *common)
        wiphy->iface_combinations = rsi_iface_combinations;
        wiphy->n_iface_combinations = ARRAY_SIZE(rsi_iface_combinations);
 
+       if (common->coex_mode > 1)
+               wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
        status = ieee80211_register_hw(hw);
        if (status)
                return status;
index c21fca7..0757adc 100644 (file)
@@ -325,8 +325,8 @@ static int rsi_load_radio_caps(struct rsi_common *common)
        radio_caps->channel_num = common->channel;
        radio_caps->rf_model = RSI_RF_TYPE;
 
+       radio_caps->radio_cfg_info = RSI_LMAC_CLOCK_80MHZ;
        if (common->channel_width == BW_40MHZ) {
-               radio_caps->radio_cfg_info = RSI_LMAC_CLOCK_80MHZ;
                radio_caps->radio_cfg_info |= RSI_ENABLE_40MHZ;
 
                if (common->fsm_state == FSM_MAC_INIT_DONE) {
@@ -454,14 +454,10 @@ static int rsi_mgmt_pkt_to_core(struct rsi_common *common,
  *
  * Return: status: 0 on success, corresponding negative error code on failure.
  */
-static int rsi_hal_send_sta_notify_frame(struct rsi_common *common,
-                                        enum opmode opmode,
-                                        u8 notify_event,
-                                        const unsigned char *bssid,
-                                        u8 qos_enable,
-                                        u16 aid,
-                                        u16 sta_id,
-                                        struct ieee80211_vif *vif)
+int rsi_hal_send_sta_notify_frame(struct rsi_common *common, enum opmode opmode,
+                                 u8 notify_event, const unsigned char *bssid,
+                                 u8 qos_enable, u16 aid, u16 sta_id,
+                                 struct ieee80211_vif *vif)
 {
        struct sk_buff *skb = NULL;
        struct rsi_peer_notify *peer_notify;
@@ -1328,6 +1324,7 @@ void rsi_inform_bss_status(struct rsi_common *common,
                           u16 aid,
                           struct ieee80211_sta *sta,
                           u16 sta_id,
+                          u16 assoc_cap,
                           struct ieee80211_vif *vif)
 {
        if (status) {
@@ -1342,10 +1339,10 @@ void rsi_inform_bss_status(struct rsi_common *common,
                                              vif);
                if (common->min_rate == 0xffff)
                        rsi_send_auto_rate_request(common, sta, sta_id, vif);
-               if (opmode == RSI_OPMODE_STA) {
-                       if (!rsi_send_block_unblock_frame(common, false))
-                               common->hw_data_qs_blocked = false;
-               }
+               if (opmode == RSI_OPMODE_STA &&
+                   !(assoc_cap & WLAN_CAPABILITY_PRIVACY) &&
+                   !rsi_send_block_unblock_frame(common, false))
+                       common->hw_data_qs_blocked = false;
        } else {
                if (opmode == RSI_OPMODE_STA)
                        common->hw_data_qs_blocked = true;
@@ -1850,10 +1847,19 @@ int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg)
                        __func__);
                return rsi_handle_card_ready(common, msg);
        case TX_STATUS_IND:
-               if (msg[15] == PROBEREQ_CONFIRM) {
+               switch (msg[RSI_TX_STATUS_TYPE]) {
+               case PROBEREQ_CONFIRM:
                        common->mgmt_q_block = false;
                        rsi_dbg(FSM_ZONE, "%s: Probe confirm received\n",
                                __func__);
+                       break;
+               case EAPOL4_CONFIRM:
+                       if (msg[RSI_TX_STATUS]) {
+                               common->eapol4_confirm = true;
+                               if (!rsi_send_block_unblock_frame(common,
+                                                                 false))
+                                       common->hw_data_qs_blocked = false;
+                       }
                }
                break;
        case BEACON_EVENT_IND:
index d76e69c..416981d 100644 (file)
@@ -170,7 +170,6 @@ static void rsi_reset_card(struct sdio_func *pfunction)
        int err;
        struct mmc_card *card = pfunction->card;
        struct mmc_host *host = card->host;
-       s32 bit = (fls(host->ocr_avail) - 1);
        u8 cmd52_resp;
        u32 clock, resp, i;
        u16 rca;
@@ -190,7 +189,6 @@ static void rsi_reset_card(struct sdio_func *pfunction)
        msleep(20);
 
        /* Initialize the SDIO card */
-       host->ios.vdd = bit;
        host->ios.chip_select = MMC_CS_DONTCARE;
        host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
        host->ios.power_mode = MMC_POWER_UP;
@@ -660,8 +658,6 @@ static int rsi_sdio_master_reg_read(struct rsi_hw *adapter, u32 addr,
        if (!data)
                return -ENOMEM;
 
-       data = PTR_ALIGN(data, 8);
-
        ms_addr = (addr >> 16);
        status = rsi_sdio_master_access_msword(adapter, ms_addr);
        if (status < 0) {
@@ -724,8 +720,6 @@ static int rsi_sdio_master_reg_write(struct rsi_hw *adapter,
        if (!data_aligned)
                return -ENOMEM;
 
-       data_aligned = PTR_ALIGN(data_aligned, 8);
-
        if (size == 2) {
                *data_aligned = ((data << 16) | (data & 0xFFFF));
        } else if (size == 1) {
@@ -1042,17 +1036,21 @@ static void ulp_read_write(struct rsi_hw *adapter, u16 addr, u32 data,
 /*This function resets and re-initializes the chip.*/
 static void rsi_reset_chip(struct rsi_hw *adapter)
 {
-       __le32 data;
+       u8 *data;
        u8 sdio_interrupt_status = 0;
        u8 request = 1;
        int ret;
 
+       data = kzalloc(sizeof(u32), GFP_KERNEL);
+       if (!data)
+               return;
+
        rsi_dbg(INFO_ZONE, "Writing disable to wakeup register\n");
        ret =  rsi_sdio_write_register(adapter, 0, SDIO_WAKEUP_REG, &request);
        if (ret < 0) {
                rsi_dbg(ERR_ZONE,
                        "%s: Failed to write SDIO wakeup register\n", __func__);
-               return;
+               goto err;
        }
        msleep(20);
        ret =  rsi_sdio_read_register(adapter, RSI_FN1_INT_REGISTER,
@@ -1060,7 +1058,7 @@ static void rsi_reset_chip(struct rsi_hw *adapter)
        if (ret < 0) {
                rsi_dbg(ERR_ZONE, "%s: Failed to Read Intr Status Register\n",
                        __func__);
-               return;
+               goto err;
        }
        rsi_dbg(INFO_ZONE, "%s: Intr Status Register value = %d\n",
                __func__, sdio_interrupt_status);
@@ -1070,17 +1068,17 @@ static void rsi_reset_chip(struct rsi_hw *adapter)
                rsi_dbg(ERR_ZONE,
                        "%s: Unable to set ms word to common reg\n",
                        __func__);
-               return;
+               goto err;
        }
 
-       data = TA_HOLD_THREAD_VALUE;
+       put_unaligned_le32(TA_HOLD_THREAD_VALUE, data);
        if (rsi_sdio_write_register_multiple(adapter, TA_HOLD_THREAD_REG |
                                             RSI_SD_REQUEST_MASTER,
-                                            (u8 *)&data, 4)) {
+                                            data, 4)) {
                rsi_dbg(ERR_ZONE,
                        "%s: Unable to hold Thread-Arch processor threads\n",
                        __func__);
-               return;
+               goto err;
        }
 
        /* This msleep will ensure Thread-Arch processor to go to hold
@@ -1101,6 +1099,9 @@ static void rsi_reset_chip(struct rsi_hw *adapter)
         * read write operations to complete for chip reset.
         */
        msleep(500);
+err:
+       kfree(data);
+       return;
 }
 
 /**
index 7b8bae3..6ce6b75 100644 (file)
@@ -687,6 +687,14 @@ static int rsi_reset_card(struct rsi_hw *adapter)
         */
        msleep(100);
 
+       ret = rsi_usb_master_reg_write(adapter, SWBL_REGOUT,
+                                      RSI_FW_WDT_DISABLE_REQ,
+                                      RSI_COMMON_REG_SIZE);
+       if (ret < 0) {
+               rsi_dbg(ERR_ZONE, "Disabling firmware watchdog timer failed\n");
+               goto fail;
+       }
+
        ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_1,
                                 RSI_ULP_WRITE_2, 32);
        if (ret < 0)
index 238ee96..ad903b2 100644 (file)
@@ -46,7 +46,8 @@
        (((TA_PLL_M_VAL_20 + 1) * 40) / \
         ((TA_PLL_N_VAL_20 + 1) * (TA_PLL_P_VAL_20 + 1)))
 #define VALID_20 \
-       (WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | WIFI_SWITCH_CLK_CONFIGS)
+       (WIFI_TAPLL_CONFIGS | WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | \
+        WIFI_SWITCH_CLK_CONFIGS | BOOTUP_MODE_INFO | CRYSTAL_GOOD_TIME)
 #define UMAC_CLK_40BW   \
        (((TA_PLL_M_VAL_40 + 1) * 40) / \
         ((TA_PLL_N_VAL_40 + 1) * (TA_PLL_P_VAL_40 + 1)))
index 786dccd..327638c 100644 (file)
 #define FW_FLASH_OFFSET                        0x820
 #define LMAC_VER_OFFSET                        (FW_FLASH_OFFSET + 0x200)
 #define MAX_DWORD_ALIGN_BYTES          64
+#define RSI_COMMON_REG_SIZE            2
 
 struct bl_header {
        __le32 flags;
@@ -167,6 +168,8 @@ struct rsi_bt_desc {
 } __packed;
 
 int rsi_hal_device_init(struct rsi_hw *adapter);
+int rsi_prepare_mgmt_desc(struct rsi_common *common, struct sk_buff *skb);
+int rsi_prepare_data_desc(struct rsi_common *common, struct sk_buff *skb);
 int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb);
 int rsi_send_pkt_to_bus(struct rsi_common *common, struct sk_buff *skb);
 int rsi_send_bt_pkt(struct rsi_common *common, struct sk_buff *skb);
index ef4fa32..a084f22 100644 (file)
@@ -190,12 +190,6 @@ struct cqm_info {
        u32 rssi_hyst;
 };
 
-struct xtended_desc {
-       u8 confirm_frame_type;
-       u8 retry_cnt;
-       u16 reserved;
-};
-
 enum rsi_dfs_regions {
        RSI_REGION_FCC = 0,
        RSI_REGION_ETSI,
@@ -293,6 +287,7 @@ struct rsi_common {
        struct timer_list roc_timer;
        struct ieee80211_vif *roc_vif;
 
+       bool eapol4_confirm;
        void *bt_adapter;
 };
 
index cf6567a..1462093 100644 (file)
@@ -33,6 +33,7 @@
 #define WMM_SHORT_SLOT_TIME             9
 #define SIFS_DURATION                   16
 
+#define EAPOL4_PACKET_LEN              0x85
 #define KEY_TYPE_CLEAR                  0
 #define RSI_PAIRWISE_KEY                1
 #define RSI_GROUP_KEY                   2
 #define RX_DOT11_MGMT                   0x02
 #define TX_STATUS_IND                   0x04
 #define BEACON_EVENT_IND               0x08
+#define EAPOL4_CONFIRM                  1
 #define PROBEREQ_CONFIRM                2
 #define CARD_READY_IND                  0x00
 #define SLEEP_NOTIFY_IND                0x06
+#define RSI_TX_STATUS_TYPE             15
+#define RSI_TX_STATUS                  12
 
 #define RSI_DELETE_PEER                 0x0
 #define RSI_ADD_PEER                    0x1
 #define RSI_WOW_DISCONNECT             BIT(5)
 #endif
 
+#define RSI_MAX_TX_AGGR_FRMS           8
+#define RSI_MAX_RX_AGGR_FRMS           8
+
 enum opmode {
        RSI_OPMODE_UNSUPPORTED = -1,
        RSI_OPMODE_AP = 0,
@@ -301,6 +308,12 @@ struct rsi_mac_frame {
 #define ENCAP_MGMT_PKT                 BIT(7)
 #define DESC_IMMEDIATE_WAKEUP          BIT(15)
 
+struct rsi_xtended_desc {
+       u8 confirm_frame_type;
+       u8 retry_cnt;
+       u16 reserved;
+};
+
 struct rsi_cmd_desc_dword0 {
        __le16 len_qno;
        u8 frame_type;
@@ -654,10 +667,14 @@ int rsi_set_channel(struct rsi_common *common,
                    struct ieee80211_channel *channel);
 int rsi_send_vap_dynamic_update(struct rsi_common *common);
 int rsi_send_block_unblock_frame(struct rsi_common *common, bool event);
+int rsi_hal_send_sta_notify_frame(struct rsi_common *common, enum opmode opmode,
+                                 u8 notify_event, const unsigned char *bssid,
+                                 u8 qos_enable, u16 aid, u16 sta_id,
+                                 struct ieee80211_vif *vif);
 void rsi_inform_bss_status(struct rsi_common *common, enum opmode opmode,
                           u8 status, const u8 *addr, u8 qos_enable, u16 aid,
                           struct ieee80211_sta *sta, u16 sta_id,
-                          struct ieee80211_vif *vif);
+                          u16 assoc_cap, struct ieee80211_vif *vif);
 void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb);
 int rsi_mac80211_attach(struct rsi_common *common);
 void rsi_indicate_tx_status(struct rsi_hw *common, struct sk_buff *skb,
index ead8e7c..353dbdf 100644 (file)
@@ -87,7 +87,7 @@ enum sdio_interrupt_type {
 #define TA_SOFT_RST_CLR              0
 #define TA_SOFT_RST_SET              BIT(0)
 #define TA_PC_ZERO                   0
-#define TA_HOLD_THREAD_VALUE         cpu_to_le32(0xF)
+#define TA_HOLD_THREAD_VALUE         0xF
 #define TA_RELEASE_THREAD_VALUE      cpu_to_le32(0xF)
 #define TA_BASE_ADDR                 0x2200
 #define MISC_CFG_BASE_ADDR           0x4105
index a88d592..b6fe79f 100644 (file)
@@ -26,6 +26,7 @@
 #define RSI_USB_READY_MAGIC_NUM      0xab
 #define FW_STATUS_REG                0x41050012
 #define RSI_TA_HOLD_REG              0x22000844
+#define RSI_FW_WDT_DISABLE_REQ      0x69
 
 #define USB_VENDOR_REGISTER_READ     0x15
 #define USB_VENDOR_REGISTER_WRITE    0x16
index e9050b4..f7b1b00 100644 (file)
@@ -1069,7 +1069,7 @@ void cw1200_rx_cb(struct cw1200_common *priv,
        }
 
        if (skb->len < sizeof(struct ieee80211_pspoll)) {
-               wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n");
+               wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than IEEE header.\n");
                goto drop;
        }
 
index 1f727ba..6dbe61d 100644 (file)
@@ -155,17 +155,11 @@ static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue)
        struct mmc_card *card = func->card;
 
        ret = pm_runtime_get_sync(&card->dev);
-       if (ret) {
-               /*
-                * Runtime PM might be temporarily disabled, or the device
-                * might have a positive reference counter. Make sure it is
-                * really powered on.
-                */
-               ret = mmc_power_restore_host(card->host);
-               if (ret < 0) {
-                       pm_runtime_put_sync(&card->dev);
-                       goto out;
-               }
+       if (ret < 0) {
+               pm_runtime_put_noidle(&card->dev);
+               dev_err(glue->dev, "%s: failed to get_sync(%d)\n",
+                       __func__, ret);
+               goto out;
        }
 
        sdio_claim_host(func);
@@ -178,7 +172,6 @@ out:
 
 static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
 {
-       int ret;
        struct sdio_func *func = dev_to_sdio_func(glue->dev);
        struct mmc_card *card = func->card;
 
@@ -186,16 +179,8 @@ static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
        sdio_disable_func(func);
        sdio_release_host(func);
 
-       /* Power off the card manually in case it wasn't powered off above */
-       ret = mmc_power_save_host(card->host);
-       if (ret < 0)
-               goto out;
-
        /* Let runtime PM know the card is powered off */
-       pm_runtime_put_sync(&card->dev);
-
-out:
-       return ret;
+       return pm_runtime_put_sync(&card->dev);
 }
 
 static int wl12xx_sdio_set_power(struct device *child, bool enable)
index a3771c5..99b857e 100644 (file)
@@ -99,6 +99,7 @@ static struct class *nvme_subsys_class;
 
 static void nvme_ns_remove(struct nvme_ns *ns);
 static int nvme_revalidate_disk(struct gendisk *disk);
+static void nvme_put_subsystem(struct nvme_subsystem *subsys);
 
 int nvme_reset_ctrl(struct nvme_ctrl *ctrl)
 {
@@ -117,7 +118,8 @@ int nvme_reset_ctrl_sync(struct nvme_ctrl *ctrl)
        ret = nvme_reset_ctrl(ctrl);
        if (!ret) {
                flush_work(&ctrl->reset_work);
-               if (ctrl->state != NVME_CTRL_LIVE)
+               if (ctrl->state != NVME_CTRL_LIVE &&
+                   ctrl->state != NVME_CTRL_ADMIN_ONLY)
                        ret = -ENETRESET;
        }
 
@@ -350,6 +352,7 @@ static void nvme_free_ns_head(struct kref *ref)
        ida_simple_remove(&head->subsys->ns_ida, head->instance);
        list_del_init(&head->entry);
        cleanup_srcu_struct(&head->srcu);
+       nvme_put_subsystem(head->subsys);
        kfree(head);
 }
 
@@ -2861,6 +2864,9 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
                goto out_cleanup_srcu;
 
        list_add_tail(&head->entry, &ctrl->subsys->nsheads);
+
+       kref_get(&ctrl->subsys->ref);
+
        return head;
 out_cleanup_srcu:
        cleanup_srcu_struct(&head->srcu);
index 7ded7a5..17d2f7c 100644 (file)
@@ -84,6 +84,11 @@ enum nvme_quirks {
         * Supports the LighNVM command set if indicated in vs[1].
         */
        NVME_QUIRK_LIGHTNVM                     = (1 << 6),
+
+       /*
+        * Set MEDIUM priority on SQ creation
+        */
+       NVME_QUIRK_MEDIUM_PRIO_SQ               = (1 << 7),
 };
 
 /*
index fbc71fa..17a0190 100644 (file)
@@ -1093,9 +1093,18 @@ static int adapter_alloc_cq(struct nvme_dev *dev, u16 qid,
 static int adapter_alloc_sq(struct nvme_dev *dev, u16 qid,
                                                struct nvme_queue *nvmeq)
 {
+       struct nvme_ctrl *ctrl = &dev->ctrl;
        struct nvme_command c;
        int flags = NVME_QUEUE_PHYS_CONTIG;
 
+       /*
+        * Some drives have a bug that auto-enables WRRU if MEDIUM isn't
+        * set. Since URGENT priority is zeroes, it makes all queues
+        * URGENT.
+        */
+       if (ctrl->quirks & NVME_QUIRK_MEDIUM_PRIO_SQ)
+               flags |= NVME_SQ_PRIO_MEDIUM;
+
        /*
         * Note: we (ab)use the fact that the prp fields survive if no data
         * is attached to the request.
@@ -2701,7 +2710,8 @@ static const struct pci_device_id nvme_id_table[] = {
                .driver_data = NVME_QUIRK_STRIPE_SIZE |
                                NVME_QUIRK_DEALLOCATE_ZEROES, },
        { PCI_VDEVICE(INTEL, 0xf1a5),   /* Intel 600P/P3100 */
-               .driver_data = NVME_QUIRK_NO_DEEPEST_PS },
+               .driver_data = NVME_QUIRK_NO_DEEPEST_PS |
+                               NVME_QUIRK_MEDIUM_PRIO_SQ },
        { PCI_VDEVICE(INTEL, 0x5845),   /* Qemu emulated controller */
                .driver_data = NVME_QUIRK_IDENTIFY_CNS, },
        { PCI_DEVICE(0x1c58, 0x0003),   /* HGST adapter */
index 8c0c927..d963baf 100644 (file)
@@ -204,6 +204,9 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
        bool scanphys = false;
        int addr, rc;
 
+       if (!np)
+               return mdiobus_register(mdio);
+
        /* Do not continue if the node is disabled */
        if (!of_device_is_available(np))
                return -ENODEV;
index 126cf19..297599f 100644 (file)
@@ -1195,7 +1195,7 @@ void * ccio_get_iommu(const struct parisc_device *dev)
  * to/from certain pages.  To avoid this happening, we mark these pages
  * as `used', and ensure that nothing will try to allocate from them.
  */
-void ccio_cujo20_fixup(struct parisc_device *cujo, u32 iovp)
+void __init ccio_cujo20_fixup(struct parisc_device *cujo, u32 iovp)
 {
        unsigned int idx;
        struct parisc_device *dev = parisc_parent(cujo);
index a0d5221..4ef4292 100644 (file)
@@ -135,19 +135,25 @@ struct mvebu_comhy_conf {
 static const struct mvebu_comhy_conf mvebu_comphy_cp110_modes[] = {
        /* lane 0 */
        MVEBU_COMPHY_CONF(0, 1, PHY_MODE_SGMII, 0x1),
+       MVEBU_COMPHY_CONF(0, 1, PHY_MODE_2500SGMII, 0x1),
        /* lane 1 */
        MVEBU_COMPHY_CONF(1, 2, PHY_MODE_SGMII, 0x1),
+       MVEBU_COMPHY_CONF(1, 2, PHY_MODE_2500SGMII, 0x1),
        /* lane 2 */
        MVEBU_COMPHY_CONF(2, 0, PHY_MODE_SGMII, 0x1),
+       MVEBU_COMPHY_CONF(2, 0, PHY_MODE_2500SGMII, 0x1),
        MVEBU_COMPHY_CONF(2, 0, PHY_MODE_10GKR, 0x1),
        /* lane 3 */
        MVEBU_COMPHY_CONF(3, 1, PHY_MODE_SGMII, 0x2),
+       MVEBU_COMPHY_CONF(3, 1, PHY_MODE_2500SGMII, 0x2),
        /* lane 4 */
        MVEBU_COMPHY_CONF(4, 0, PHY_MODE_SGMII, 0x2),
+       MVEBU_COMPHY_CONF(4, 0, PHY_MODE_2500SGMII, 0x2),
        MVEBU_COMPHY_CONF(4, 0, PHY_MODE_10GKR, 0x2),
        MVEBU_COMPHY_CONF(4, 1, PHY_MODE_SGMII, 0x1),
        /* lane 5 */
        MVEBU_COMPHY_CONF(5, 2, PHY_MODE_SGMII, 0x1),
+       MVEBU_COMPHY_CONF(5, 2, PHY_MODE_2500SGMII, 0x1),
 };
 
 struct mvebu_comphy_priv {
@@ -206,6 +212,10 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
        if (mode == PHY_MODE_10GKR)
                val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xe) |
                       MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xe);
+       else if (mode == PHY_MODE_2500SGMII)
+               val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x8) |
+                      MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x8) |
+                      MVEBU_COMPHY_SERDES_CFG0_HALF_BUS;
        else if (mode == PHY_MODE_SGMII)
                val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x6) |
                       MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x6) |
@@ -296,13 +306,13 @@ static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane,
        return 0;
 }
 
-static int mvebu_comphy_set_mode_sgmii(struct phy *phy)
+static int mvebu_comphy_set_mode_sgmii(struct phy *phy, enum phy_mode mode)
 {
        struct mvebu_comphy_lane *lane = phy_get_drvdata(phy);
        struct mvebu_comphy_priv *priv = lane->priv;
        u32 val;
 
-       mvebu_comphy_ethernet_init_reset(lane, PHY_MODE_SGMII);
+       mvebu_comphy_ethernet_init_reset(lane, mode);
 
        val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id));
        val &= ~MVEBU_COMPHY_RX_CTRL1_CLK8T_EN;
@@ -487,7 +497,8 @@ static int mvebu_comphy_power_on(struct phy *phy)
 
        switch (lane->mode) {
        case PHY_MODE_SGMII:
-               ret = mvebu_comphy_set_mode_sgmii(phy);
+       case PHY_MODE_2500SGMII:
+               ret = mvebu_comphy_set_mode_sgmii(phy, lane->mode);
                break;
        case PHY_MODE_10GKR:
                ret = mvebu_comphy_set_mode_10gkr(phy);
index bc309c5..566644b 100644 (file)
@@ -168,8 +168,8 @@ config DELL_WMI
        depends on DMI
        depends on INPUT
        depends on ACPI_VIDEO || ACPI_VIDEO = n
+       depends on DELL_SMBIOS
        select DELL_WMI_DESCRIPTOR
-       select DELL_SMBIOS
        select INPUT_SPARSEKMAP
        ---help---
          Say Y here if you want to support WMI-based hotkeys on Dell laptops.
index 360e06b..ac18f2f 100644 (file)
@@ -110,7 +110,7 @@ static const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
        UNIPHIER_RESETX(4, 0x200c, 2),          /* eMMC */
        UNIPHIER_RESETX(6, 0x200c, 6),          /* Ether */
        UNIPHIER_RESETX(8, 0x200c, 8),          /* STDMAC (HSC) */
-       UNIPHIER_RESETX(12, 0x200c, 5),         /* GIO (PCIe, USB3) */
+       UNIPHIER_RESETX(14, 0x200c, 5),         /* USB30 */
        UNIPHIER_RESETX(16, 0x200c, 12),        /* USB30-PHY0 */
        UNIPHIER_RESETX(17, 0x200c, 13),        /* USB30-PHY1 */
        UNIPHIER_RESETX(18, 0x200c, 14),        /* USB30-PHY2 */
@@ -127,8 +127,8 @@ static const struct uniphier_reset_data uniphier_pxs3_sys_reset_data[] = {
        UNIPHIER_RESETX(6, 0x200c, 9),          /* Ether0 */
        UNIPHIER_RESETX(7, 0x200c, 10),         /* Ether1 */
        UNIPHIER_RESETX(8, 0x200c, 12),         /* STDMAC */
-       UNIPHIER_RESETX(12, 0x200c, 4),         /* USB30 link (GIO0) */
-       UNIPHIER_RESETX(13, 0x200c, 5),         /* USB31 link (GIO1) */
+       UNIPHIER_RESETX(12, 0x200c, 4),         /* USB30 link */
+       UNIPHIER_RESETX(13, 0x200c, 5),         /* USB31 link */
        UNIPHIER_RESETX(16, 0x200c, 16),        /* USB30-PHY0 */
        UNIPHIER_RESETX(17, 0x200c, 18),        /* USB30-PHY1 */
        UNIPHIER_RESETX(18, 0x200c, 20),        /* USB30-PHY2 */
index 439991d..4c14ce4 100644 (file)
@@ -141,7 +141,7 @@ static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues)
        int i;
 
        for (i = 0; i < nr_queues; i++) {
-               q = kmem_cache_alloc(qdio_q_cache, GFP_KERNEL);
+               q = kmem_cache_zalloc(qdio_q_cache, GFP_KERNEL);
                if (!q)
                        return -ENOMEM;
 
@@ -456,7 +456,6 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
 {
        struct ciw *ciw;
        struct qdio_irq *irq_ptr = init_data->cdev->private->qdio_data;
-       int rc;
 
        memset(&irq_ptr->qib, 0, sizeof(irq_ptr->qib));
        memset(&irq_ptr->siga_flag, 0, sizeof(irq_ptr->siga_flag));
@@ -493,16 +492,14 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
        ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_EQUEUE);
        if (!ciw) {
                DBF_ERROR("%4x NO EQ", irq_ptr->schid.sch_no);
-               rc = -EINVAL;
-               goto out_err;
+               return -EINVAL;
        }
        irq_ptr->equeue = *ciw;
 
        ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_AQUEUE);
        if (!ciw) {
                DBF_ERROR("%4x NO AQ", irq_ptr->schid.sch_no);
-               rc = -EINVAL;
-               goto out_err;
+               return -EINVAL;
        }
        irq_ptr->aqueue = *ciw;
 
@@ -512,9 +509,6 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
        init_data->cdev->handler = qdio_int_handler;
        spin_unlock_irq(get_ccwdev_lock(irq_ptr->cdev));
        return 0;
-out_err:
-       qdio_release_memory(irq_ptr);
-       return rc;
 }
 
 void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
index 2c75507..dce92b2 100644 (file)
@@ -715,6 +715,10 @@ void cp_free(struct channel_program *cp)
  * and stores the result to ccwchain list. @cp must have been
  * initialized by a previous call with cp_init(). Otherwise, undefined
  * behavior occurs.
+ * For each chain composing the channel program:
+ * - On entry ch_len holds the count of CCWs to be translated.
+ * - On exit ch_len is adjusted to the count of successfully translated CCWs.
+ * This allows cp_free to find in ch_len the count of CCWs to free in a chain.
  *
  * The S/390 CCW Translation APIS (prefixed by 'cp_') are introduced
  * as helpers to do ccw chain translation inside the kernel. Basically
@@ -749,11 +753,18 @@ int cp_prefetch(struct channel_program *cp)
                for (idx = 0; idx < len; idx++) {
                        ret = ccwchain_fetch_one(chain, idx, cp);
                        if (ret)
-                               return ret;
+                               goto out_err;
                }
        }
 
        return 0;
+out_err:
+       /* Only cleanup the chain elements that were actually translated. */
+       chain->ch_len = idx;
+       list_for_each_entry_continue(chain, &cp->ccwchain_list, next) {
+               chain->ch_len = 0;
+       }
+       return ret;
 }
 
 /**
index 0156c96..d62ddd6 100644 (file)
@@ -724,6 +724,8 @@ int aac_hba_send(u8 command, struct fib *fibptr, fib_callback callback,
        int wait;
        unsigned long flags = 0;
        unsigned long mflags = 0;
+       struct aac_hba_cmd_req *hbacmd = (struct aac_hba_cmd_req *)
+                       fibptr->hw_fib_va;
 
        fibptr->flags = (FIB_CONTEXT_FLAG | FIB_CONTEXT_FLAG_NATIVE_HBA);
        if (callback) {
@@ -734,11 +736,9 @@ int aac_hba_send(u8 command, struct fib *fibptr, fib_callback callback,
                wait = 1;
 
 
-       if (command == HBA_IU_TYPE_SCSI_CMD_REQ) {
-               struct aac_hba_cmd_req *hbacmd =
-                       (struct aac_hba_cmd_req *)fibptr->hw_fib_va;
+       hbacmd->iu_type = command;
 
-               hbacmd->iu_type = command;
+       if (command == HBA_IU_TYPE_SCSI_CMD_REQ) {
                /* bit1 of request_id must be 0 */
                hbacmd->request_id =
                        cpu_to_le32((((u32)(fibptr - dev->fibs)) << 2) + 1);
index 96bbb82..a10cf25 100644 (file)
@@ -1500,8 +1500,8 @@ fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16)
        CAP16_TO_CAP32(FC_RX);
        CAP16_TO_CAP32(FC_TX);
        CAP16_TO_CAP32(ANEG);
-       CAP16_TO_CAP32(MDIX);
        CAP16_TO_CAP32(MDIAUTO);
+       CAP16_TO_CAP32(MDISTRAIGHT);
        CAP16_TO_CAP32(FEC_RS);
        CAP16_TO_CAP32(FEC_BASER_RS);
        CAP16_TO_CAP32(802_3_PAUSE);
index c105a2e..cabb6af 100644 (file)
@@ -383,11 +383,16 @@ struct qedf_ctx {
        u32 flogi_failed;
 
        /* Used for fc statistics */
+       struct mutex stats_mutex;
        u64 input_requests;
        u64 output_requests;
        u64 control_requests;
        u64 packet_aborts;
        u64 alloc_failures;
+       u8 lun_resets;
+       u8 target_resets;
+       u8 task_set_fulls;
+       u8 busy;
 };
 
 struct io_bdt {
@@ -496,7 +501,9 @@ extern int qedf_post_io_req(struct qedf_rport *fcport,
 extern void qedf_process_seq_cleanup_compl(struct qedf_ctx *qedf,
        struct fcoe_cqe *cqe, struct qedf_ioreq *io_req);
 extern int qedf_send_flogi(struct qedf_ctx *qedf);
+extern void qedf_get_protocol_tlv_data(void *dev, void *data);
 extern void qedf_fp_io_handler(struct work_struct *work);
+extern void qedf_get_generic_tlv_data(void *dev, struct qed_generic_tlvs *data);
 
 #define FCOE_WORD_TO_BYTE  4
 #define QEDF_MAX_TASK_NUM      0xFFFF
index c539a7a..5789ce1 100644 (file)
@@ -439,7 +439,6 @@ qedf_dbg_offload_stats_open(struct inode *inode, struct file *file)
        return single_open(file, qedf_offload_stats_show, qedf);
 }
 
-
 const struct file_operations qedf_dbg_fops[] = {
        qedf_dbg_fileops(qedf, fp_int),
        qedf_dbg_fileops_seq(qedf, io_trace),
index 50a50c4..3fe579d 100644 (file)
@@ -1200,6 +1200,12 @@ void qedf_scsi_completion(struct qedf_ctx *qedf, struct fcoe_cqe *cqe,
                                        fcport->retry_delay_timestamp =
                                            jiffies + (qualifier * HZ / 10);
                                }
+                               /* Record stats */
+                               if (io_req->cdb_status ==
+                                   SAM_STAT_TASK_SET_FULL)
+                                       qedf->task_set_fulls++;
+                               else
+                                       qedf->busy++;
                        }
                }
                if (io_req->fcp_resid)
@@ -1866,6 +1872,11 @@ static int qedf_execute_tmf(struct qedf_rport *fcport, struct scsi_cmnd *sc_cmd,
                goto reset_tmf_err;
        }
 
+       if (tm_flags == FCP_TMF_LUN_RESET)
+               qedf->lun_resets++;
+       else if (tm_flags == FCP_TMF_TGT_RESET)
+               qedf->target_resets++;
+
        /* Initialize rest of io_req fields */
        io_req->sc_cmd = sc_cmd;
        io_req->fcport = fcport;
index 6c19015..d3f73d8 100644 (file)
@@ -566,6 +566,8 @@ static struct qed_fcoe_cb_ops qedf_cb_ops = {
        {
                .link_update = qedf_link_update,
                .dcbx_aen = qedf_dcbx_handler,
+               .get_generic_tlv_data = qedf_get_generic_tlv_data,
+               .get_protocol_tlv_data = qedf_get_protocol_tlv_data,
        }
 };
 
@@ -1746,6 +1748,8 @@ static struct fc_host_statistics *qedf_fc_get_host_stats(struct Scsi_Host
                goto out;
        }
 
+       mutex_lock(&qedf->stats_mutex);
+
        /* Query firmware for offload stats */
        qed_ops->get_stats(qedf->cdev, fw_fcoe_stats);
 
@@ -1779,6 +1783,7 @@ static struct fc_host_statistics *qedf_fc_get_host_stats(struct Scsi_Host
        qedf_stats->fcp_packet_aborts += qedf->packet_aborts;
        qedf_stats->fcp_frame_alloc_failures += qedf->alloc_failures;
 
+       mutex_unlock(&qedf->stats_mutex);
        kfree(fw_fcoe_stats);
 out:
        return qedf_stats;
@@ -2948,6 +2953,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
                qedf->stop_io_on_error = false;
                pci_set_drvdata(pdev, qedf);
                init_completion(&qedf->fipvlan_compl);
+               mutex_init(&qedf->stats_mutex);
 
                QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO,
                   "QLogic FastLinQ FCoE Module qedf %s, "
@@ -3392,6 +3398,104 @@ static void qedf_remove(struct pci_dev *pdev)
        __qedf_remove(pdev, QEDF_MODE_NORMAL);
 }
 
+/*
+ * Protocol TLV handler
+ */
+void qedf_get_protocol_tlv_data(void *dev, void *data)
+{
+       struct qedf_ctx *qedf = dev;
+       struct qed_mfw_tlv_fcoe *fcoe = data;
+       struct fc_lport *lport = qedf->lport;
+       struct Scsi_Host *host = lport->host;
+       struct fc_host_attrs *fc_host = shost_to_fc_host(host);
+       struct fc_host_statistics *hst;
+
+       /* Force a refresh of the fc_host stats including offload stats */
+       hst = qedf_fc_get_host_stats(host);
+
+       fcoe->qos_pri_set = true;
+       fcoe->qos_pri = 3; /* Hard coded to 3 in driver */
+
+       fcoe->ra_tov_set = true;
+       fcoe->ra_tov = lport->r_a_tov;
+
+       fcoe->ed_tov_set = true;
+       fcoe->ed_tov = lport->e_d_tov;
+
+       fcoe->npiv_state_set = true;
+       fcoe->npiv_state = 1; /* NPIV always enabled */
+
+       fcoe->num_npiv_ids_set = true;
+       fcoe->num_npiv_ids = fc_host->npiv_vports_inuse;
+
+       /* Certain attributes we only want to set if we've selected an FCF */
+       if (qedf->ctlr.sel_fcf) {
+               fcoe->switch_name_set = true;
+               u64_to_wwn(qedf->ctlr.sel_fcf->switch_name, fcoe->switch_name);
+       }
+
+       fcoe->port_state_set = true;
+       /* For qedf we're either link down or fabric attach */
+       if (lport->link_up)
+               fcoe->port_state = QED_MFW_TLV_PORT_STATE_FABRIC;
+       else
+               fcoe->port_state = QED_MFW_TLV_PORT_STATE_OFFLINE;
+
+       fcoe->link_failures_set = true;
+       fcoe->link_failures = (u16)hst->link_failure_count;
+
+       fcoe->fcoe_txq_depth_set = true;
+       fcoe->fcoe_rxq_depth_set = true;
+       fcoe->fcoe_rxq_depth = FCOE_PARAMS_NUM_TASKS;
+       fcoe->fcoe_txq_depth = FCOE_PARAMS_NUM_TASKS;
+
+       fcoe->fcoe_rx_frames_set = true;
+       fcoe->fcoe_rx_frames = hst->rx_frames;
+
+       fcoe->fcoe_tx_frames_set = true;
+       fcoe->fcoe_tx_frames = hst->tx_frames;
+
+       fcoe->fcoe_rx_bytes_set = true;
+       fcoe->fcoe_rx_bytes = hst->fcp_input_megabytes * 1000000;
+
+       fcoe->fcoe_tx_bytes_set = true;
+       fcoe->fcoe_tx_bytes = hst->fcp_output_megabytes * 1000000;
+
+       fcoe->crc_count_set = true;
+       fcoe->crc_count = hst->invalid_crc_count;
+
+       fcoe->tx_abts_set = true;
+       fcoe->tx_abts = hst->fcp_packet_aborts;
+
+       fcoe->tx_lun_rst_set = true;
+       fcoe->tx_lun_rst = qedf->lun_resets;
+
+       fcoe->abort_task_sets_set = true;
+       fcoe->abort_task_sets = qedf->packet_aborts;
+
+       fcoe->scsi_busy_set = true;
+       fcoe->scsi_busy = qedf->busy;
+
+       fcoe->scsi_tsk_full_set = true;
+       fcoe->scsi_tsk_full = qedf->task_set_fulls;
+}
+
+/* Generic TLV data callback */
+void qedf_get_generic_tlv_data(void *dev, struct qed_generic_tlvs *data)
+{
+       struct qedf_ctx *qedf;
+
+       if (!dev) {
+               QEDF_INFO(NULL, QEDF_LOG_EVT,
+                         "dev is NULL so ignoring get_generic_tlv_data request.\n");
+               return;
+       }
+       qedf = (struct qedf_ctx *)dev;
+
+       memset(data, 0, sizeof(struct qed_generic_tlvs));
+       ether_addr_copy(data->mac[0], qedf->mac);
+}
+
 /*
  * Module Init/Remove
  */
index b8b22ce..fc3babc 100644 (file)
@@ -353,6 +353,9 @@ struct qedi_ctx {
 #define IPV6_LEN       41
 #define IPV4_LEN       17
        struct iscsi_boot_kset *boot_kset;
+
+       /* Used for iscsi statistics */
+       struct mutex stats_lock;
 };
 
 struct qedi_work {
index ea13151..1126077 100644 (file)
@@ -223,6 +223,12 @@ struct qedi_work_map {
        struct work_struct *ptr_tmf_work;
 };
 
+struct qedi_boot_target {
+       char ip_addr[64];
+       char iscsi_name[255];
+       u32 ipv6_en;
+};
+
 #define qedi_set_itt(task_id, itt) ((u32)(((task_id) & 0xffff) | ((itt) << 16)))
 #define qedi_get_itt(cqe) (cqe.iscsi_hdr.cmd.itt >> 16)
 
index 4da3592..32ee7f6 100644 (file)
@@ -55,6 +55,7 @@ static void qedi_free_global_queues(struct qedi_ctx *qedi);
 static struct qedi_cmd *qedi_get_cmd_from_tid(struct qedi_ctx *qedi, u32 tid);
 static void qedi_reset_uio_rings(struct qedi_uio_dev *udev);
 static void qedi_ll2_free_skbs(struct qedi_ctx *qedi);
+static struct nvm_iscsi_block *qedi_get_nvram_block(struct qedi_ctx *qedi);
 
 static int qedi_iscsi_event_cb(void *context, u8 fw_event_code, void *fw_handle)
 {
@@ -879,6 +880,201 @@ static void qedi_free_iscsi_pf_param(struct qedi_ctx *qedi)
        kfree(qedi->global_queues);
 }
 
+static void qedi_get_boot_tgt_info(struct nvm_iscsi_block *block,
+                                  struct qedi_boot_target *tgt, u8 index)
+{
+       u32 ipv6_en;
+
+       ipv6_en = !!(block->generic.ctrl_flags &
+                    NVM_ISCSI_CFG_GEN_IPV6_ENABLED);
+
+       snprintf(tgt->iscsi_name, NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN, "%s\n",
+                block->target[index].target_name.byte);
+
+       tgt->ipv6_en = ipv6_en;
+
+       if (ipv6_en)
+               snprintf(tgt->ip_addr, IPV6_LEN, "%pI6\n",
+                        block->target[index].ipv6_addr.byte);
+       else
+               snprintf(tgt->ip_addr, IPV4_LEN, "%pI4\n",
+                        block->target[index].ipv4_addr.byte);
+}
+
+static int qedi_find_boot_info(struct qedi_ctx *qedi,
+                              struct qed_mfw_tlv_iscsi *iscsi,
+                              struct nvm_iscsi_block *block)
+{
+       struct qedi_boot_target *pri_tgt = NULL, *sec_tgt = NULL;
+       u32 pri_ctrl_flags = 0, sec_ctrl_flags = 0, found = 0;
+       struct iscsi_cls_session *cls_sess;
+       struct iscsi_cls_conn *cls_conn;
+       struct qedi_conn *qedi_conn;
+       struct iscsi_session *sess;
+       struct iscsi_conn *conn;
+       char ep_ip_addr[64];
+       int i, ret = 0;
+
+       pri_ctrl_flags = !!(block->target[0].ctrl_flags &
+                                       NVM_ISCSI_CFG_TARGET_ENABLED);
+       if (pri_ctrl_flags) {
+               pri_tgt = kzalloc(sizeof(*pri_tgt), GFP_KERNEL);
+               if (!pri_tgt)
+                       return -1;
+               qedi_get_boot_tgt_info(block, pri_tgt, 0);
+       }
+
+       sec_ctrl_flags = !!(block->target[1].ctrl_flags &
+                                       NVM_ISCSI_CFG_TARGET_ENABLED);
+       if (sec_ctrl_flags) {
+               sec_tgt = kzalloc(sizeof(*sec_tgt), GFP_KERNEL);
+               if (!sec_tgt) {
+                       ret = -1;
+                       goto free_tgt;
+               }
+               qedi_get_boot_tgt_info(block, sec_tgt, 1);
+       }
+
+       for (i = 0; i < qedi->max_active_conns; i++) {
+               qedi_conn = qedi_get_conn_from_id(qedi, i);
+               if (!qedi_conn)
+                       continue;
+
+               if (qedi_conn->ep->ip_type == TCP_IPV4)
+                       snprintf(ep_ip_addr, IPV4_LEN, "%pI4\n",
+                                qedi_conn->ep->dst_addr);
+               else
+                       snprintf(ep_ip_addr, IPV6_LEN, "%pI6\n",
+                                qedi_conn->ep->dst_addr);
+
+               cls_conn = qedi_conn->cls_conn;
+               conn = cls_conn->dd_data;
+               cls_sess = iscsi_conn_to_session(cls_conn);
+               sess = cls_sess->dd_data;
+
+               if (pri_ctrl_flags) {
+                       if (!strcmp(pri_tgt->iscsi_name, sess->targetname) &&
+                           !strcmp(pri_tgt->ip_addr, ep_ip_addr)) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (sec_ctrl_flags) {
+                       if (!strcmp(sec_tgt->iscsi_name, sess->targetname) &&
+                           !strcmp(sec_tgt->ip_addr, ep_ip_addr)) {
+                               found = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (found) {
+               if (conn->hdrdgst_en) {
+                       iscsi->header_digest_set = true;
+                       iscsi->header_digest = 1;
+               }
+
+               if (conn->datadgst_en) {
+                       iscsi->data_digest_set = true;
+                       iscsi->data_digest = 1;
+               }
+               iscsi->boot_taget_portal_set = true;
+               iscsi->boot_taget_portal = sess->tpgt;
+
+       } else {
+               ret = -1;
+       }
+
+       if (sec_ctrl_flags)
+               kfree(sec_tgt);
+free_tgt:
+       if (pri_ctrl_flags)
+               kfree(pri_tgt);
+
+       return ret;
+}
+
+static void qedi_get_generic_tlv_data(void *dev, struct qed_generic_tlvs *data)
+{
+       struct qedi_ctx *qedi;
+
+       if (!dev) {
+               QEDI_INFO(NULL, QEDI_LOG_EVT,
+                         "dev is NULL so ignoring get_generic_tlv_data request.\n");
+               return;
+       }
+       qedi = (struct qedi_ctx *)dev;
+
+       memset(data, 0, sizeof(struct qed_generic_tlvs));
+       ether_addr_copy(data->mac[0], qedi->mac);
+}
+
+/*
+ * Protocol TLV handler
+ */
+static void qedi_get_protocol_tlv_data(void *dev, void *data)
+{
+       struct qed_mfw_tlv_iscsi *iscsi = data;
+       struct qed_iscsi_stats *fw_iscsi_stats;
+       struct nvm_iscsi_block *block = NULL;
+       u32 chap_en = 0, mchap_en = 0;
+       struct qedi_ctx *qedi = dev;
+       int rval = 0;
+
+       fw_iscsi_stats = kmalloc(sizeof(*fw_iscsi_stats), GFP_KERNEL);
+       if (!fw_iscsi_stats) {
+               QEDI_ERR(&qedi->dbg_ctx,
+                        "Could not allocate memory for fw_iscsi_stats.\n");
+               goto exit_get_data;
+       }
+
+       mutex_lock(&qedi->stats_lock);
+       /* Query firmware for offload stats */
+       qedi_ops->get_stats(qedi->cdev, fw_iscsi_stats);
+       mutex_unlock(&qedi->stats_lock);
+
+       iscsi->rx_frames_set = true;
+       iscsi->rx_frames = fw_iscsi_stats->iscsi_rx_packet_cnt;
+       iscsi->rx_bytes_set = true;
+       iscsi->rx_bytes = fw_iscsi_stats->iscsi_rx_bytes_cnt;
+       iscsi->tx_frames_set = true;
+       iscsi->tx_frames = fw_iscsi_stats->iscsi_tx_packet_cnt;
+       iscsi->tx_bytes_set = true;
+       iscsi->tx_bytes = fw_iscsi_stats->iscsi_tx_bytes_cnt;
+       iscsi->frame_size_set = true;
+       iscsi->frame_size = qedi->ll2_mtu;
+       block = qedi_get_nvram_block(qedi);
+       if (block) {
+               chap_en = !!(block->generic.ctrl_flags &
+                            NVM_ISCSI_CFG_GEN_CHAP_ENABLED);
+               mchap_en = !!(block->generic.ctrl_flags &
+                             NVM_ISCSI_CFG_GEN_CHAP_MUTUAL_ENABLED);
+
+               iscsi->auth_method_set = (chap_en || mchap_en) ? true : false;
+               iscsi->auth_method = 1;
+               if (chap_en)
+                       iscsi->auth_method = 2;
+               if (mchap_en)
+                       iscsi->auth_method = 3;
+
+               iscsi->tx_desc_size_set = true;
+               iscsi->tx_desc_size = QEDI_SQ_SIZE;
+               iscsi->rx_desc_size_set = true;
+               iscsi->rx_desc_size = QEDI_CQ_SIZE;
+
+               /* tpgt, hdr digest, data digest */
+               rval = qedi_find_boot_info(qedi, iscsi, block);
+               if (rval)
+                       QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
+                                 "Boot target not set");
+       }
+
+       kfree(fw_iscsi_stats);
+exit_get_data:
+       return;
+}
+
 static void qedi_link_update(void *dev, struct qed_link_output *link)
 {
        struct qedi_ctx *qedi = (struct qedi_ctx *)dev;
@@ -896,6 +1092,8 @@ static void qedi_link_update(void *dev, struct qed_link_output *link)
 static struct qed_iscsi_cb_ops qedi_cb_ops = {
        {
                .link_update =          qedi_link_update,
+               .get_protocol_tlv_data = qedi_get_protocol_tlv_data,
+               .get_generic_tlv_data = qedi_get_generic_tlv_data,
        }
 };
 
index c374e3b..777e5f1 100644 (file)
@@ -609,7 +609,7 @@ static void pvscsi_complete_request(struct pvscsi_adapter *adapter,
                        break;
 
                case BTSTAT_ABORTQUEUE:
-                       cmd->result = (DID_ABORT << 16);
+                       cmd->result = (DID_BUS_BUSY << 16);
                        break;
 
                case BTSTAT_SCSIPARITY:
index 1596d35..6573152 100644 (file)
@@ -490,7 +490,7 @@ static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi,
 
 static void bcm_qspi_enable_bspi(struct bcm_qspi *qspi)
 {
-       if (!has_bspi(qspi) || (qspi->bspi_enabled))
+       if (!has_bspi(qspi))
                return;
 
        qspi->bspi_enabled = 1;
@@ -505,7 +505,7 @@ static void bcm_qspi_enable_bspi(struct bcm_qspi *qspi)
 
 static void bcm_qspi_disable_bspi(struct bcm_qspi *qspi)
 {
-       if (!has_bspi(qspi) || (!qspi->bspi_enabled))
+       if (!has_bspi(qspi))
                return;
 
        qspi->bspi_enabled = 0;
@@ -519,16 +519,19 @@ static void bcm_qspi_disable_bspi(struct bcm_qspi *qspi)
 
 static void bcm_qspi_chip_select(struct bcm_qspi *qspi, int cs)
 {
-       u32 data = 0;
+       u32 rd = 0;
+       u32 wr = 0;
 
-       if (qspi->curr_cs == cs)
-               return;
        if (qspi->base[CHIP_SELECT]) {
-               data = bcm_qspi_read(qspi, CHIP_SELECT, 0);
-               data = (data & ~0xff) | (1 << cs);
-               bcm_qspi_write(qspi, CHIP_SELECT, 0, data);
+               rd = bcm_qspi_read(qspi, CHIP_SELECT, 0);
+               wr = (rd & ~0xff) | (1 << cs);
+               if (rd == wr)
+                       return;
+               bcm_qspi_write(qspi, CHIP_SELECT, 0, wr);
                usleep_range(10, 20);
        }
+
+       dev_dbg(&qspi->pdev->dev, "using cs:%d\n", cs);
        qspi->curr_cs = cs;
 }
 
@@ -755,8 +758,13 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi)
                        dev_dbg(&qspi->pdev->dev, "WR %04x\n", val);
                }
                mspi_cdram = MSPI_CDRAM_CONT_BIT;
-               mspi_cdram |= (~(1 << spi->chip_select) &
-                              MSPI_CDRAM_PCS);
+
+               if (has_bspi(qspi))
+                       mspi_cdram &= ~1;
+               else
+                       mspi_cdram |= (~(1 << spi->chip_select) &
+                                      MSPI_CDRAM_PCS);
+
                mspi_cdram |= ((tp.trans->bits_per_word <= 8) ? 0 :
                                MSPI_CDRAM_BITSE_BIT);
 
index 1431cb9..3094d81 100644 (file)
@@ -184,6 +184,11 @@ static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id)
        struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
        irqreturn_t ret = IRQ_NONE;
 
+       /* IRQ may be shared, so return if our interrupts are disabled */
+       if (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_CNTL1) &
+             (BCM2835_AUX_SPI_CNTL1_TXEMPTY | BCM2835_AUX_SPI_CNTL1_IDLE)))
+               return ret;
+
        /* check if we have data to read */
        while (bs->rx_len &&
               (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) &
index 5c9516a..4a00163 100644 (file)
@@ -313,6 +313,14 @@ static void cdns_spi_fill_tx_fifo(struct cdns_spi *xspi)
 
        while ((trans_cnt < CDNS_SPI_FIFO_DEPTH) &&
               (xspi->tx_bytes > 0)) {
+
+               /* When xspi in busy condition, bytes may send failed,
+                * then spi control did't work thoroughly, add one byte delay
+                */
+               if (cdns_spi_read(xspi, CDNS_SPI_ISR) &
+                   CDNS_SPI_IXR_TXFULL)
+                       usleep_range(10, 20);
+
                if (xspi->txbuf)
                        cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
                else
index 6f57592..a056ee8 100644 (file)
@@ -1701,7 +1701,7 @@ static struct platform_driver spi_imx_driver = {
 };
 module_platform_driver(spi_imx_driver);
 
-MODULE_DESCRIPTION("SPI Master Controller driver");
+MODULE_DESCRIPTION("SPI Controller driver");
 MODULE_AUTHOR("Sascha Hauer, Pengutronix");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:" DRIVER_NAME);
index 513ec6c..0ae7def 100644 (file)
@@ -38,7 +38,7 @@ struct driver_data {
 
        /* SSP register addresses */
        void __iomem *ioaddr;
-       u32 ssdr_physical;
+       phys_addr_t ssdr_physical;
 
        /* SSP masks*/
        u32 dma_cr1;
index ae086aa..8171eed 100644 (file)
@@ -283,6 +283,7 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
        }
 
        k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_div_table) - 1);
+       brps = min_t(int, brps, 32);
 
        scr = sh_msiof_spi_div_table[k].brdv | SCR_BRPS(brps);
        sh_msiof_write(p, TSCR, scr);
index 0124a91..dd46b75 100644 (file)
@@ -238,6 +238,17 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params,
                        if (IS_ERR(shm))
                                return PTR_ERR(shm);
 
+                       /*
+                        * Ensure offset + size does not overflow offset
+                        * and does not overflow the size of the referred
+                        * shared memory object.
+                        */
+                       if ((ip.a + ip.b) < ip.a ||
+                           (ip.a + ip.b) > shm->size) {
+                               tee_shm_put(shm);
+                               return -EINVAL;
+                       }
+
                        params[n].u.memref.shm_offs = ip.a;
                        params[n].u.memref.size = ip.b;
                        params[n].u.memref.shm = shm;
index 556960a..07d3be6 100644 (file)
@@ -360,9 +360,10 @@ int tee_shm_get_fd(struct tee_shm *shm)
        if (!(shm->flags & TEE_SHM_DMA_BUF))
                return -EINVAL;
 
+       get_dma_buf(shm->dmabuf);
        fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC);
-       if (fd >= 0)
-               get_dma_buf(shm->dmabuf);
+       if (fd < 0)
+               dma_buf_put(shm->dmabuf);
        return fd;
 }
 
index 8a7f24d..0c19fcd 100644 (file)
@@ -194,6 +194,7 @@ static int int3403_cdev_add(struct int3403_priv *priv)
                return -EFAULT;
        }
 
+       priv->priv = obj;
        obj->max_state = p->package.count - 1;
        obj->cdev =
                thermal_cooling_device_register(acpi_device_bid(priv->adev),
@@ -201,8 +202,6 @@ static int int3403_cdev_add(struct int3403_priv *priv)
        if (IS_ERR(obj->cdev))
                result = PTR_ERR(obj->cdev);
 
-       priv->priv = obj;
-
        kfree(buf.pointer);
        /* TODO: add ACPI notification support */
 
index ed805c7..ac83f72 100644 (file)
  * @regulator: pointer to the TMU regulator structure.
  * @reg_conf: pointer to structure to register with core thermal.
  * @ntrip: number of supported trip points.
+ * @enabled: current status of TMU device
  * @tmu_initialize: SoC specific TMU initialization method
  * @tmu_control: SoC specific TMU control method
  * @tmu_read: SoC specific TMU temperature read method
@@ -205,6 +206,7 @@ struct exynos_tmu_data {
        struct regulator *regulator;
        struct thermal_zone_device *tzd;
        unsigned int ntrip;
+       bool enabled;
 
        int (*tmu_initialize)(struct platform_device *pdev);
        void (*tmu_control)(struct platform_device *pdev, bool on);
@@ -398,6 +400,7 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
        mutex_lock(&data->lock);
        clk_enable(data->clk);
        data->tmu_control(pdev, on);
+       data->enabled = on;
        clk_disable(data->clk);
        mutex_unlock(&data->lock);
 }
@@ -889,19 +892,24 @@ static void exynos7_tmu_control(struct platform_device *pdev, bool on)
 static int exynos_get_temp(void *p, int *temp)
 {
        struct exynos_tmu_data *data = p;
+       int value, ret = 0;
 
-       if (!data || !data->tmu_read)
+       if (!data || !data->tmu_read || !data->enabled)
                return -EINVAL;
 
        mutex_lock(&data->lock);
        clk_enable(data->clk);
 
-       *temp = code_to_temp(data, data->tmu_read(data)) * MCELSIUS;
+       value = data->tmu_read(data);
+       if (value < 0)
+               ret = value;
+       else
+               *temp = code_to_temp(data, value) * MCELSIUS;
 
        clk_disable(data->clk);
        mutex_unlock(&data->lock);
 
-       return 0;
+       return ret;
 }
 
 #ifdef CONFIG_THERMAL_EMULATION
index 72ebbc9..32cd52c 100644 (file)
@@ -354,7 +354,7 @@ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
 
        slot_id = 0;
        for (i = 0; i < MAX_HC_SLOTS; i++) {
-               if (!xhci->devs[i])
+               if (!xhci->devs[i] || !xhci->devs[i]->udev)
                        continue;
                speed = xhci->devs[i]->udev->speed;
                if (((speed >= USB_SPEED_SUPER) == (hcd->speed >= HCD_USB3))
index e7f99d5..15a42ce 100644 (file)
@@ -2524,8 +2524,11 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
 {
        struct musb     *musb = hcd_to_musb(hcd);
        u8              devctl;
+       int             ret;
 
-       musb_port_suspend(musb, true);
+       ret = musb_port_suspend(musb, true);
+       if (ret)
+               return ret;
 
        if (!is_host_active(musb))
                return 0;
index 72392bb..2999845 100644 (file)
@@ -67,7 +67,7 @@ extern void musb_host_rx(struct musb *, u8);
 extern void musb_root_disconnect(struct musb *musb);
 extern void musb_host_resume_root_hub(struct musb *musb);
 extern void musb_host_poke_root_hub(struct musb *musb);
-extern void musb_port_suspend(struct musb *musb, bool do_suspend);
+extern int musb_port_suspend(struct musb *musb, bool do_suspend);
 extern void musb_port_reset(struct musb *musb, bool do_reset);
 extern void musb_host_finish_resume(struct work_struct *work);
 #else
@@ -99,7 +99,10 @@ static inline void musb_root_disconnect(struct musb *musb)   {}
 static inline void musb_host_resume_root_hub(struct musb *musb)        {}
 static inline void musb_host_poll_rh_status(struct musb *musb) {}
 static inline void musb_host_poke_root_hub(struct musb *musb)  {}
-static inline void musb_port_suspend(struct musb *musb, bool do_suspend) {}
+static inline int musb_port_suspend(struct musb *musb, bool do_suspend)
+{
+       return 0;
+}
 static inline void musb_port_reset(struct musb *musb, bool do_reset) {}
 static inline void musb_host_finish_resume(struct work_struct *work) {}
 #endif
index 5165d2b..2f8dd98 100644 (file)
@@ -48,14 +48,14 @@ void musb_host_finish_resume(struct work_struct *work)
        spin_unlock_irqrestore(&musb->lock, flags);
 }
 
-void musb_port_suspend(struct musb *musb, bool do_suspend)
+int musb_port_suspend(struct musb *musb, bool do_suspend)
 {
        struct usb_otg  *otg = musb->xceiv->otg;
        u8              power;
        void __iomem    *mbase = musb->mregs;
 
        if (!is_host_active(musb))
-               return;
+               return 0;
 
        /* NOTE:  this doesn't necessarily put PHY into low power mode,
         * turning off its clock; that's a function of PHY integration and
@@ -66,16 +66,20 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
        if (do_suspend) {
                int retries = 10000;
 
-               power &= ~MUSB_POWER_RESUME;
-               power |= MUSB_POWER_SUSPENDM;
-               musb_writeb(mbase, MUSB_POWER, power);
+               if (power & MUSB_POWER_RESUME)
+                       return -EBUSY;
 
-               /* Needed for OPT A tests */
-               power = musb_readb(mbase, MUSB_POWER);
-               while (power & MUSB_POWER_SUSPENDM) {
+               if (!(power & MUSB_POWER_SUSPENDM)) {
+                       power |= MUSB_POWER_SUSPENDM;
+                       musb_writeb(mbase, MUSB_POWER, power);
+
+                       /* Needed for OPT A tests */
                        power = musb_readb(mbase, MUSB_POWER);
-                       if (retries-- < 1)
-                               break;
+                       while (power & MUSB_POWER_SUSPENDM) {
+                               power = musb_readb(mbase, MUSB_POWER);
+                               if (retries-- < 1)
+                                       break;
+                       }
                }
 
                musb_dbg(musb, "Root port suspended, power %02x", power);
@@ -111,6 +115,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
                schedule_delayed_work(&musb->finish_resume_work,
                                      msecs_to_jiffies(USB_RESUME_TIMEOUT));
        }
+       return 0;
 }
 
 void musb_port_reset(struct musb *musb, bool do_reset)
index 14a7235..35618ce 100644 (file)
@@ -73,6 +73,7 @@ struct bus_id_priv {
        struct stub_device *sdev;
        struct usb_device *udev;
        char shutdown_busid;
+       spinlock_t busid_lock;
 };
 
 /* stub_priv is allocated from stub_priv_cache */
@@ -83,6 +84,7 @@ extern struct usb_device_driver stub_driver;
 
 /* stub_main.c */
 struct bus_id_priv *get_busid_priv(const char *busid);
+void put_busid_priv(struct bus_id_priv *bid);
 int del_match_busid(char *busid);
 void stub_device_cleanup_urbs(struct stub_device *sdev);
 
index dd8ef36..c0d6ff1 100644 (file)
@@ -300,9 +300,9 @@ static int stub_probe(struct usb_device *udev)
        struct stub_device *sdev = NULL;
        const char *udev_busid = dev_name(&udev->dev);
        struct bus_id_priv *busid_priv;
-       int rc;
+       int rc = 0;
 
-       dev_dbg(&udev->dev, "Enter\n");
+       dev_dbg(&udev->dev, "Enter probe\n");
 
        /* check we should claim or not by busid_table */
        busid_priv = get_busid_priv(udev_busid);
@@ -317,13 +317,15 @@ static int stub_probe(struct usb_device *udev)
                 * other matched drivers by the driver core.
                 * See driver_probe_device() in driver/base/dd.c
                 */
-               return -ENODEV;
+               rc = -ENODEV;
+               goto call_put_busid_priv;
        }
 
        if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
                dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
                         udev_busid);
-               return -ENODEV;
+               rc = -ENODEV;
+               goto call_put_busid_priv;
        }
 
        if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
@@ -331,13 +333,16 @@ static int stub_probe(struct usb_device *udev)
                        "%s is attached on vhci_hcd... skip!\n",
                        udev_busid);
 
-               return -ENODEV;
+               rc = -ENODEV;
+               goto call_put_busid_priv;
        }
 
        /* ok, this is my device */
        sdev = stub_device_alloc(udev);
-       if (!sdev)
-               return -ENOMEM;
+       if (!sdev) {
+               rc = -ENOMEM;
+               goto call_put_busid_priv;
+       }
 
        dev_info(&udev->dev,
                "usbip-host: register new device (bus %u dev %u)\n",
@@ -369,7 +374,9 @@ static int stub_probe(struct usb_device *udev)
        }
        busid_priv->status = STUB_BUSID_ALLOC;
 
-       return 0;
+       rc = 0;
+       goto call_put_busid_priv;
+
 err_files:
        usb_hub_release_port(udev->parent, udev->portnum,
                             (struct usb_dev_state *) udev);
@@ -379,6 +386,9 @@ err_port:
 
        busid_priv->sdev = NULL;
        stub_device_free(sdev);
+
+call_put_busid_priv:
+       put_busid_priv(busid_priv);
        return rc;
 }
 
@@ -404,7 +414,7 @@ static void stub_disconnect(struct usb_device *udev)
        struct bus_id_priv *busid_priv;
        int rc;
 
-       dev_dbg(&udev->dev, "Enter\n");
+       dev_dbg(&udev->dev, "Enter disconnect\n");
 
        busid_priv = get_busid_priv(udev_busid);
        if (!busid_priv) {
@@ -417,7 +427,7 @@ static void stub_disconnect(struct usb_device *udev)
        /* get stub_device */
        if (!sdev) {
                dev_err(&udev->dev, "could not get device");
-               return;
+               goto call_put_busid_priv;
        }
 
        dev_set_drvdata(&udev->dev, NULL);
@@ -432,12 +442,12 @@ static void stub_disconnect(struct usb_device *udev)
                                  (struct usb_dev_state *) udev);
        if (rc) {
                dev_dbg(&udev->dev, "unable to release port\n");
-               return;
+               goto call_put_busid_priv;
        }
 
        /* If usb reset is called from event handler */
        if (usbip_in_eh(current))
-               return;
+               goto call_put_busid_priv;
 
        /* shutdown the current connection */
        shutdown_busid(busid_priv);
@@ -448,12 +458,11 @@ static void stub_disconnect(struct usb_device *udev)
        busid_priv->sdev = NULL;
        stub_device_free(sdev);
 
-       if (busid_priv->status == STUB_BUSID_ALLOC) {
+       if (busid_priv->status == STUB_BUSID_ALLOC)
                busid_priv->status = STUB_BUSID_ADDED;
-       } else {
-               busid_priv->status = STUB_BUSID_OTHER;
-               del_match_busid((char *)udev_busid);
-       }
+
+call_put_busid_priv:
+       put_busid_priv(busid_priv);
 }
 
 #ifdef CONFIG_PM
index d41d0cd..bf8a5fe 100644 (file)
@@ -14,6 +14,7 @@
 #define DRIVER_DESC "USB/IP Host Driver"
 
 struct kmem_cache *stub_priv_cache;
+
 /*
  * busid_tables defines matching busids that usbip can grab. A user can change
  * dynamically what device is locally used and what device is exported to a
@@ -25,6 +26,8 @@ static spinlock_t busid_table_lock;
 
 static void init_busid_table(void)
 {
+       int i;
+
        /*
         * This also sets the bus_table[i].status to
         * STUB_BUSID_OTHER, which is 0.
@@ -32,6 +35,9 @@ static void init_busid_table(void)
        memset(busid_table, 0, sizeof(busid_table));
 
        spin_lock_init(&busid_table_lock);
+
+       for (i = 0; i < MAX_BUSID; i++)
+               spin_lock_init(&busid_table[i].busid_lock);
 }
 
 /*
@@ -43,15 +49,20 @@ static int get_busid_idx(const char *busid)
        int i;
        int idx = -1;
 
-       for (i = 0; i < MAX_BUSID; i++)
+       for (i = 0; i < MAX_BUSID; i++) {
+               spin_lock(&busid_table[i].busid_lock);
                if (busid_table[i].name[0])
                        if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
                                idx = i;
+                               spin_unlock(&busid_table[i].busid_lock);
                                break;
                        }
+               spin_unlock(&busid_table[i].busid_lock);
+       }
        return idx;
 }
 
+/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
 struct bus_id_priv *get_busid_priv(const char *busid)
 {
        int idx;
@@ -59,13 +70,22 @@ struct bus_id_priv *get_busid_priv(const char *busid)
 
        spin_lock(&busid_table_lock);
        idx = get_busid_idx(busid);
-       if (idx >= 0)
+       if (idx >= 0) {
                bid = &(busid_table[idx]);
+               /* get busid_lock before returning */
+               spin_lock(&bid->busid_lock);
+       }
        spin_unlock(&busid_table_lock);
 
        return bid;
 }
 
+void put_busid_priv(struct bus_id_priv *bid)
+{
+       if (bid)
+               spin_unlock(&bid->busid_lock);
+}
+
 static int add_match_busid(char *busid)
 {
        int i;
@@ -78,15 +98,19 @@ static int add_match_busid(char *busid)
                goto out;
        }
 
-       for (i = 0; i < MAX_BUSID; i++)
+       for (i = 0; i < MAX_BUSID; i++) {
+               spin_lock(&busid_table[i].busid_lock);
                if (!busid_table[i].name[0]) {
                        strlcpy(busid_table[i].name, busid, BUSID_SIZE);
                        if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
                            (busid_table[i].status != STUB_BUSID_REMOV))
                                busid_table[i].status = STUB_BUSID_ADDED;
                        ret = 0;
+                       spin_unlock(&busid_table[i].busid_lock);
                        break;
                }
+               spin_unlock(&busid_table[i].busid_lock);
+       }
 
 out:
        spin_unlock(&busid_table_lock);
@@ -107,6 +131,8 @@ int del_match_busid(char *busid)
        /* found */
        ret = 0;
 
+       spin_lock(&busid_table[idx].busid_lock);
+
        if (busid_table[idx].status == STUB_BUSID_OTHER)
                memset(busid_table[idx].name, 0, BUSID_SIZE);
 
@@ -114,6 +140,7 @@ int del_match_busid(char *busid)
            (busid_table[idx].status != STUB_BUSID_ADDED))
                busid_table[idx].status = STUB_BUSID_REMOV;
 
+       spin_unlock(&busid_table[idx].busid_lock);
 out:
        spin_unlock(&busid_table_lock);
 
@@ -126,9 +153,12 @@ static ssize_t match_busid_show(struct device_driver *drv, char *buf)
        char *out = buf;
 
        spin_lock(&busid_table_lock);
-       for (i = 0; i < MAX_BUSID; i++)
+       for (i = 0; i < MAX_BUSID; i++) {
+               spin_lock(&busid_table[i].busid_lock);
                if (busid_table[i].name[0])
                        out += sprintf(out, "%s ", busid_table[i].name);
+               spin_unlock(&busid_table[i].busid_lock);
+       }
        spin_unlock(&busid_table_lock);
        out += sprintf(out, "\n");
 
@@ -169,6 +199,51 @@ static ssize_t match_busid_store(struct device_driver *dev, const char *buf,
 }
 static DRIVER_ATTR_RW(match_busid);
 
+static int do_rebind(char *busid, struct bus_id_priv *busid_priv)
+{
+       int ret;
+
+       /* device_attach() callers should hold parent lock for USB */
+       if (busid_priv->udev->dev.parent)
+               device_lock(busid_priv->udev->dev.parent);
+       ret = device_attach(&busid_priv->udev->dev);
+       if (busid_priv->udev->dev.parent)
+               device_unlock(busid_priv->udev->dev.parent);
+       if (ret < 0) {
+               dev_err(&busid_priv->udev->dev, "rebind failed\n");
+               return ret;
+       }
+       return 0;
+}
+
+static void stub_device_rebind(void)
+{
+#if IS_MODULE(CONFIG_USBIP_HOST)
+       struct bus_id_priv *busid_priv;
+       int i;
+
+       /* update status to STUB_BUSID_OTHER so probe ignores the device */
+       spin_lock(&busid_table_lock);
+       for (i = 0; i < MAX_BUSID; i++) {
+               if (busid_table[i].name[0] &&
+                   busid_table[i].shutdown_busid) {
+                       busid_priv = &(busid_table[i]);
+                       busid_priv->status = STUB_BUSID_OTHER;
+               }
+       }
+       spin_unlock(&busid_table_lock);
+
+       /* now run rebind - no need to hold locks. driver files are removed */
+       for (i = 0; i < MAX_BUSID; i++) {
+               if (busid_table[i].name[0] &&
+                   busid_table[i].shutdown_busid) {
+                       busid_priv = &(busid_table[i]);
+                       do_rebind(busid_table[i].name, busid_priv);
+               }
+       }
+#endif
+}
+
 static ssize_t rebind_store(struct device_driver *dev, const char *buf,
                                 size_t count)
 {
@@ -186,16 +261,17 @@ static ssize_t rebind_store(struct device_driver *dev, const char *buf,
        if (!bid)
                return -ENODEV;
 
-       /* device_attach() callers should hold parent lock for USB */
-       if (bid->udev->dev.parent)
-               device_lock(bid->udev->dev.parent);
-       ret = device_attach(&bid->udev->dev);
-       if (bid->udev->dev.parent)
-               device_unlock(bid->udev->dev.parent);
-       if (ret < 0) {
-               dev_err(&bid->udev->dev, "rebind failed\n");
+       /* mark the device for deletion so probe ignores it during rescan */
+       bid->status = STUB_BUSID_OTHER;
+       /* release the busid lock */
+       put_busid_priv(bid);
+
+       ret = do_rebind((char *) buf, bid);
+       if (ret < 0)
                return ret;
-       }
+
+       /* delete device from busid_table */
+       del_match_busid((char *) buf);
 
        return count;
 }
@@ -317,6 +393,9 @@ static void __exit usbip_host_exit(void)
         */
        usb_deregister_device_driver(&stub_driver);
 
+       /* initiate scan to attach devices */
+       stub_device_rebind();
+
        kmem_cache_destroy(stub_priv_cache);
 }
 
index 3bedfed..7587fb6 100644 (file)
@@ -121,7 +121,7 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len,
        p = text;
        do {
                struct sockaddr_rxrpc *srx = &alist->addrs[alist->nr_addrs];
-               char tdelim = delim;
+               const char *q, *stop;
 
                if (*p == delim) {
                        p++;
@@ -130,28 +130,33 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len,
 
                if (*p == '[') {
                        p++;
-                       tdelim = ']';
+                       q = memchr(p, ']', end - p);
+               } else {
+                       for (q = p; q < end; q++)
+                               if (*q == '+' || *q == delim)
+                                       break;
                }
 
-               if (in4_pton(p, end - p,
+               if (in4_pton(p, q - p,
                             (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
-                            tdelim, &p)) {
+                            -1, &stop)) {
                        srx->transport.sin6.sin6_addr.s6_addr32[0] = 0;
                        srx->transport.sin6.sin6_addr.s6_addr32[1] = 0;
                        srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
-               } else if (in6_pton(p, end - p,
+               } else if (in6_pton(p, q - p,
                                    srx->transport.sin6.sin6_addr.s6_addr,
-                                   tdelim, &p)) {
+                                   -1, &stop)) {
                        /* Nothing to do */
                } else {
                        goto bad_address;
                }
 
-               if (tdelim == ']') {
-                       if (p == end || *p != ']')
-                               goto bad_address;
+               if (stop != q)
+                       goto bad_address;
+
+               p = q;
+               if (q < end && *q == ']')
                        p++;
-               }
 
                if (p < end) {
                        if (*p == '+') {
index abd9a84..571437d 100644 (file)
 /*
  * Set up an interest-in-callbacks record for a volume on a server and
  * register it with the server.
- * - Called with volume->server_sem held.
+ * - Called with vnode->io_lock held.
  */
 int afs_register_server_cb_interest(struct afs_vnode *vnode,
-                                   struct afs_server_entry *entry)
+                                   struct afs_server_list *slist,
+                                   unsigned int index)
 {
-       struct afs_cb_interest *cbi = entry->cb_interest, *vcbi, *new, *x;
+       struct afs_server_entry *entry = &slist->servers[index];
+       struct afs_cb_interest *cbi, *vcbi, *new, *old;
        struct afs_server *server = entry->server;
 
 again:
+       if (vnode->cb_interest &&
+           likely(vnode->cb_interest == entry->cb_interest))
+               return 0;
+
+       read_lock(&slist->lock);
+       cbi = afs_get_cb_interest(entry->cb_interest);
+       read_unlock(&slist->lock);
+
        vcbi = vnode->cb_interest;
        if (vcbi) {
-               if (vcbi == cbi)
+               if (vcbi == cbi) {
+                       afs_put_cb_interest(afs_v2net(vnode), cbi);
                        return 0;
+               }
 
+               /* Use a new interest in the server list for the same server
+                * rather than an old one that's still attached to a vnode.
+                */
                if (cbi && vcbi->server == cbi->server) {
                        write_seqlock(&vnode->cb_lock);
-                       vnode->cb_interest = afs_get_cb_interest(cbi);
+                       old = vnode->cb_interest;
+                       vnode->cb_interest = cbi;
                        write_sequnlock(&vnode->cb_lock);
-                       afs_put_cb_interest(afs_v2net(vnode), cbi);
+                       afs_put_cb_interest(afs_v2net(vnode), old);
                        return 0;
                }
 
+               /* Re-use the one attached to the vnode. */
                if (!cbi && vcbi->server == server) {
-                       afs_get_cb_interest(vcbi);
-                       x = cmpxchg(&entry->cb_interest, cbi, vcbi);
-                       if (x != cbi) {
-                               cbi = x;
-                               afs_put_cb_interest(afs_v2net(vnode), vcbi);
+                       write_lock(&slist->lock);
+                       if (entry->cb_interest) {
+                               write_unlock(&slist->lock);
+                               afs_put_cb_interest(afs_v2net(vnode), cbi);
                                goto again;
                        }
+
+                       entry->cb_interest = cbi;
+                       write_unlock(&slist->lock);
                        return 0;
                }
        }
@@ -72,13 +91,16 @@ again:
                list_add_tail(&new->cb_link, &server->cb_interests);
                write_unlock(&server->cb_break_lock);
 
-               x = cmpxchg(&entry->cb_interest, cbi, new);
-               if (x == cbi) {
+               write_lock(&slist->lock);
+               if (!entry->cb_interest) {
+                       entry->cb_interest = afs_get_cb_interest(new);
                        cbi = new;
+                       new = NULL;
                } else {
-                       cbi = x;
-                       afs_put_cb_interest(afs_v2net(vnode), new);
+                       cbi = afs_get_cb_interest(entry->cb_interest);
                }
+               write_unlock(&slist->lock);
+               afs_put_cb_interest(afs_v2net(vnode), new);
        }
 
        ASSERT(cbi);
@@ -88,11 +110,14 @@ again:
         */
        write_seqlock(&vnode->cb_lock);
 
-       vnode->cb_interest = afs_get_cb_interest(cbi);
+       old = vnode->cb_interest;
+       vnode->cb_interest = cbi;
        vnode->cb_s_break = cbi->server->cb_s_break;
+       vnode->cb_v_break = vnode->volume->cb_v_break;
        clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
 
        write_sequnlock(&vnode->cb_lock);
+       afs_put_cb_interest(afs_v2net(vnode), old);
        return 0;
 }
 
@@ -171,13 +196,24 @@ static void afs_break_one_callback(struct afs_server *server,
                if (cbi->vid != fid->vid)
                        continue;
 
-               data.volume = NULL;
-               data.fid = *fid;
-               inode = ilookup5_nowait(cbi->sb, fid->vnode, afs_iget5_test, &data);
-               if (inode) {
-                       vnode = AFS_FS_I(inode);
-                       afs_break_callback(vnode);
-                       iput(inode);
+               if (fid->vnode == 0 && fid->unique == 0) {
+                       /* The callback break applies to an entire volume. */
+                       struct afs_super_info *as = AFS_FS_S(cbi->sb);
+                       struct afs_volume *volume = as->volume;
+
+                       write_lock(&volume->cb_break_lock);
+                       volume->cb_v_break++;
+                       write_unlock(&volume->cb_break_lock);
+               } else {
+                       data.volume = NULL;
+                       data.fid = *fid;
+                       inode = ilookup5_nowait(cbi->sb, fid->vnode,
+                                               afs_iget5_test, &data);
+                       if (inode) {
+                               vnode = AFS_FS_I(inode);
+                               afs_break_callback(vnode);
+                               iput(inode);
+                       }
                }
        }
 
@@ -195,6 +231,8 @@ void afs_break_callbacks(struct afs_server *server, size_t count,
        ASSERT(server != NULL);
        ASSERTCMP(count, <=, AFSCBMAX);
 
+       /* TODO: Sort the callback break list by volume ID */
+
        for (; count > 0; callbacks++, count--) {
                _debug("- Fid { vl=%08x n=%u u=%u }  CB { v=%u x=%u t=%u }",
                       callbacks->fid.vid,
index 357de90..c332c95 100644 (file)
@@ -133,21 +133,10 @@ bool afs_cm_incoming_call(struct afs_call *call)
 }
 
 /*
- * clean up a cache manager call
+ * Clean up a cache manager call.
  */
 static void afs_cm_destructor(struct afs_call *call)
 {
-       _enter("");
-
-       /* Break the callbacks here so that we do it after the final ACK is
-        * received.  The step number here must match the final number in
-        * afs_deliver_cb_callback().
-        */
-       if (call->unmarshall == 5) {
-               ASSERT(call->cm_server && call->count && call->request);
-               afs_break_callbacks(call->cm_server, call->count, call->request);
-       }
-
        kfree(call->buffer);
        call->buffer = NULL;
 }
@@ -161,14 +150,14 @@ static void SRXAFSCB_CallBack(struct work_struct *work)
 
        _enter("");
 
-       /* be sure to send the reply *before* attempting to spam the AFS server
-        * with FSFetchStatus requests on the vnodes with broken callbacks lest
-        * the AFS server get into a vicious cycle of trying to break further
-        * callbacks because it hadn't received completion of the CBCallBack op
-        * yet */
-       afs_send_empty_reply(call);
+       /* We need to break the callbacks before sending the reply as the
+        * server holds up change visibility till it receives our reply so as
+        * to maintain cache coherency.
+        */
+       if (call->cm_server)
+               afs_break_callbacks(call->cm_server, call->count, call->request);
 
-       afs_break_callbacks(call->cm_server, call->count, call->request);
+       afs_send_empty_reply(call);
        afs_put_call(call);
        _leave("");
 }
@@ -180,7 +169,6 @@ static int afs_deliver_cb_callback(struct afs_call *call)
 {
        struct afs_callback_break *cb;
        struct sockaddr_rxrpc srx;
-       struct afs_server *server;
        __be32 *bp;
        int ret, loop;
 
@@ -267,15 +255,6 @@ static int afs_deliver_cb_callback(struct afs_call *call)
 
                call->offset = 0;
                call->unmarshall++;
-
-               /* Record that the message was unmarshalled successfully so
-                * that the call destructor can know do the callback breaking
-                * work, even if the final ACK isn't received.
-                *
-                * If the step number changes, then afs_cm_destructor() must be
-                * updated also.
-                */
-               call->unmarshall++;
        case 5:
                break;
        }
@@ -286,10 +265,9 @@ static int afs_deliver_cb_callback(struct afs_call *call)
        /* we'll need the file server record as that tells us which set of
         * vnodes to operate upon */
        rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
-       server = afs_find_server(call->net, &srx);
-       if (!server)
-               return -ENOTCONN;
-       call->cm_server = server;
+       call->cm_server = afs_find_server(call->net, &srx);
+       if (!call->cm_server)
+               trace_afs_cm_no_server(call, &srx);
 
        return afs_queue_call_work(call);
 }
@@ -303,7 +281,8 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)
 
        _enter("{%p}", call->cm_server);
 
-       afs_init_callback_state(call->cm_server);
+       if (call->cm_server)
+               afs_init_callback_state(call->cm_server);
        afs_send_empty_reply(call);
        afs_put_call(call);
        _leave("");
@@ -315,7 +294,6 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)
 static int afs_deliver_cb_init_call_back_state(struct afs_call *call)
 {
        struct sockaddr_rxrpc srx;
-       struct afs_server *server;
        int ret;
 
        _enter("");
@@ -328,10 +306,9 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *call)
 
        /* we'll need the file server record as that tells us which set of
         * vnodes to operate upon */
-       server = afs_find_server(call->net, &srx);
-       if (!server)
-               return -ENOTCONN;
-       call->cm_server = server;
+       call->cm_server = afs_find_server(call->net, &srx);
+       if (!call->cm_server)
+               trace_afs_cm_no_server(call, &srx);
 
        return afs_queue_call_work(call);
 }
@@ -341,8 +318,6 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *call)
  */
 static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)
 {
-       struct sockaddr_rxrpc srx;
-       struct afs_server *server;
        struct afs_uuid *r;
        unsigned loop;
        __be32 *b;
@@ -398,11 +373,11 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)
 
        /* we'll need the file server record as that tells us which set of
         * vnodes to operate upon */
-       rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
-       server = afs_find_server(call->net, &srx);
-       if (!server)
-               return -ENOTCONN;
-       call->cm_server = server;
+       rcu_read_lock();
+       call->cm_server = afs_find_server_by_uuid(call->net, call->request);
+       rcu_read_unlock();
+       if (!call->cm_server)
+               trace_afs_cm_no_server_u(call, call->request);
 
        return afs_queue_call_work(call);
 }
index 5889f70..7d62300 100644 (file)
@@ -180,6 +180,7 @@ static int afs_dir_open(struct inode *inode, struct file *file)
  * get reclaimed during the iteration.
  */
 static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
+       __acquires(&dvnode->validate_lock)
 {
        struct afs_read *req;
        loff_t i_size;
@@ -261,18 +262,21 @@ retry:
        /* If we're going to reload, we need to lock all the pages to prevent
         * races.
         */
-       if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
-               ret = -ERESTARTSYS;
-               for (i = 0; i < req->nr_pages; i++)
-                       if (lock_page_killable(req->pages[i]) < 0)
-                               goto error_unlock;
+       ret = -ERESTARTSYS;
+       if (down_read_killable(&dvnode->validate_lock) < 0)
+               goto error;
 
-               if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
-                       goto success;
+       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
+               goto success;
+
+       up_read(&dvnode->validate_lock);
+       if (down_write_killable(&dvnode->validate_lock) < 0)
+               goto error;
 
+       if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
                ret = afs_fetch_data(dvnode, key, req);
                if (ret < 0)
-                       goto error_unlock_all;
+                       goto error_unlock;
 
                task_io_account_read(PAGE_SIZE * req->nr_pages);
 
@@ -284,33 +288,26 @@ retry:
                for (i = 0; i < req->nr_pages; i++)
                        if (!afs_dir_check_page(dvnode, req->pages[i],
                                                req->actual_len))
-                               goto error_unlock_all;
+                               goto error_unlock;
 
                // TODO: Trim excess pages
 
                set_bit(AFS_VNODE_DIR_VALID, &dvnode->flags);
        }
 
+       downgrade_write(&dvnode->validate_lock);
 success:
-       i = req->nr_pages;
-       while (i > 0)
-               unlock_page(req->pages[--i]);
        return req;
 
-error_unlock_all:
-       i = req->nr_pages;
 error_unlock:
-       while (i > 0)
-               unlock_page(req->pages[--i]);
+       up_write(&dvnode->validate_lock);
 error:
        afs_put_read(req);
        _leave(" = %d", ret);
        return ERR_PTR(ret);
 
 content_has_grown:
-       i = req->nr_pages;
-       while (i > 0)
-               unlock_page(req->pages[--i]);
+       up_write(&dvnode->validate_lock);
        afs_put_read(req);
        goto retry;
 }
@@ -473,6 +470,7 @@ static int afs_dir_iterate(struct inode *dir, struct dir_context *ctx,
        }
 
 out:
+       up_read(&dvnode->validate_lock);
        afs_put_read(req);
        _leave(" = %d", ret);
        return ret;
@@ -1143,7 +1141,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, dvnode, key)) {
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(dvnode);
                        afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
                                      &newfid, &newstatus, &newcb);
                }
@@ -1213,7 +1211,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, dvnode, key)) {
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(dvnode);
                        afs_fs_remove(&fc, dentry->d_name.name, true,
                                      data_version);
                }
@@ -1316,7 +1314,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, dvnode, key)) {
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(dvnode);
                        afs_fs_remove(&fc, dentry->d_name.name, false,
                                      data_version);
                }
@@ -1373,7 +1371,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, dvnode, key)) {
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(dvnode);
                        afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
                                      &newfid, &newstatus, &newcb);
                }
@@ -1443,8 +1441,8 @@ static int afs_link(struct dentry *from, struct inode *dir,
                }
 
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
-                       fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(dvnode);
+                       fc.cb_break_2 = afs_calc_vnode_cb_break(vnode);
                        afs_fs_link(&fc, vnode, dentry->d_name.name, data_version);
                }
 
@@ -1512,7 +1510,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, dvnode, key)) {
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(dvnode);
                        afs_fs_symlink(&fc, dentry->d_name.name,
                                       content, data_version,
                                       &newfid, &newstatus);
@@ -1588,8 +1586,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        }
                }
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break;
-                       fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(orig_dvnode);
+                       fc.cb_break_2 = afs_calc_vnode_cb_break(new_dvnode);
                        afs_fs_rename(&fc, old_dentry->d_name.name,
                                      new_dvnode, new_dentry->d_name.name,
                                      orig_data_version, new_data_version);
index c24c080..7d4f261 100644 (file)
@@ -238,7 +238,7 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, vnode, key)) {
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(vnode);
                        afs_fs_fetch_data(&fc, desc);
                }
 
index 7a0e017..dc62d15 100644 (file)
@@ -86,7 +86,7 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, vnode, key)) {
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(vnode);
                        afs_fs_set_lock(&fc, type);
                }
 
@@ -117,7 +117,7 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, vnode, key)) {
                while (afs_select_current_fileserver(&fc)) {
-                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(vnode);
                        afs_fs_extend_lock(&fc);
                }
 
@@ -148,7 +148,7 @@ static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, vnode, key)) {
                while (afs_select_current_fileserver(&fc)) {
-                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(vnode);
                        afs_fs_release_lock(&fc);
                }
 
index efacdb7..b273e1d 100644 (file)
@@ -134,6 +134,7 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,
                                     struct afs_read *read_req)
 {
        const struct afs_xdr_AFSFetchStatus *xdr = (const void *)*_bp;
+       bool inline_error = (call->operation_ID == afs_FS_InlineBulkStatus);
        u64 data_version, size;
        u32 type, abort_code;
        u8 flags = 0;
@@ -142,13 +143,32 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,
        if (vnode)
                write_seqlock(&vnode->cb_lock);
 
+       abort_code = ntohl(xdr->abort_code);
+
        if (xdr->if_version != htonl(AFS_FSTATUS_VERSION)) {
+               if (xdr->if_version == htonl(0) &&
+                   abort_code != 0 &&
+                   inline_error) {
+                       /* The OpenAFS fileserver has a bug in FS.InlineBulkStatus
+                        * whereby it doesn't set the interface version in the error
+                        * case.
+                        */
+                       status->abort_code = abort_code;
+                       ret = 0;
+                       goto out;
+               }
+
                pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version));
                goto bad;
        }
 
+       if (abort_code != 0 && inline_error) {
+               status->abort_code = abort_code;
+               ret = 0;
+               goto out;
+       }
+
        type = ntohl(xdr->type);
-       abort_code = ntohl(xdr->abort_code);
        switch (type) {
        case AFS_FTYPE_FILE:
        case AFS_FTYPE_DIR:
@@ -165,13 +185,6 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,
                }
                status->type = type;
                break;
-       case AFS_FTYPE_INVALID:
-               if (abort_code != 0) {
-                       status->abort_code = abort_code;
-                       ret = 0;
-                       goto out;
-               }
-               /* Fall through */
        default:
                goto bad;
        }
@@ -248,7 +261,7 @@ static void xdr_decode_AFSCallBack(struct afs_call *call,
 
        write_seqlock(&vnode->cb_lock);
 
-       if (call->cb_break == (vnode->cb_break + cbi->server->cb_s_break)) {
+       if (call->cb_break == afs_cb_break_sum(vnode, cbi)) {
                vnode->cb_version       = ntohl(*bp++);
                cb_expiry               = ntohl(*bp++);
                vnode->cb_type          = ntohl(*bp++);
index 06194cf..479b7fd 100644 (file)
@@ -108,7 +108,7 @@ int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool new_inode)
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, vnode, key)) {
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(vnode);
                        afs_fs_fetch_file_status(&fc, NULL, new_inode);
                }
 
@@ -393,15 +393,18 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
        read_seqlock_excl(&vnode->cb_lock);
 
        if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
-               if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) {
+               if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break ||
+                   vnode->cb_v_break != vnode->volume->cb_v_break) {
                        vnode->cb_s_break = vnode->cb_interest->server->cb_s_break;
+                       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;
+                       valid = true;
                } else if (!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) &&
                           vnode->cb_expires_at - 10 > now) {
-                               valid = true;
+                       valid = true;
                }
        } else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
                valid = true;
@@ -415,7 +418,7 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
        if (valid)
                goto valid;
 
-       mutex_lock(&vnode->validate_lock);
+       down_write(&vnode->validate_lock);
 
        /* if the promise has expired, we need to check the server again to get
         * a new promise - note that if the (parent) directory's metadata was
@@ -444,13 +447,13 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
         * different */
        if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags))
                afs_zap_data(vnode);
-       mutex_unlock(&vnode->validate_lock);
+       up_write(&vnode->validate_lock);
 valid:
        _leave(" = 0");
        return 0;
 
 error_unlock:
-       mutex_unlock(&vnode->validate_lock);
+       up_write(&vnode->validate_lock);
        _leave(" = %d", ret);
        return ret;
 }
@@ -574,7 +577,7 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, vnode, key)) {
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(vnode);
                        afs_fs_setattr(&fc, attr);
                }
 
index f8086ec..e3f8a46 100644 (file)
@@ -396,6 +396,7 @@ struct afs_server {
 #define AFS_SERVER_FL_PROBED   5               /* The fileserver has been probed */
 #define AFS_SERVER_FL_PROBING  6               /* Fileserver is being probed */
 #define AFS_SERVER_FL_NO_IBULK 7               /* Fileserver doesn't support FS.InlineBulkStatus */
+#define AFS_SERVER_FL_MAY_HAVE_CB 8            /* May have callbacks on this fileserver */
        atomic_t                usage;
        u32                     addr_version;   /* Address list version */
 
@@ -433,6 +434,7 @@ struct afs_server_list {
        unsigned short          index;          /* Server currently in use */
        unsigned short          vnovol_mask;    /* Servers to be skipped due to VNOVOL */
        unsigned int            seq;            /* Set to ->servers_seq when installed */
+       rwlock_t                lock;
        struct afs_server_entry servers[];
 };
 
@@ -459,6 +461,9 @@ struct afs_volume {
        rwlock_t                servers_lock;   /* Lock for ->servers */
        unsigned int            servers_seq;    /* Incremented each time ->servers changes */
 
+       unsigned                cb_v_break;     /* Break-everything counter. */
+       rwlock_t                cb_break_lock;
+
        afs_voltype_t           type;           /* type of volume */
        short                   error;
        char                    type_force;     /* force volume type (suppress R/O -> R/W) */
@@ -494,7 +499,7 @@ struct afs_vnode {
 #endif
        struct afs_permits __rcu *permit_cache; /* cache of permits so far obtained */
        struct mutex            io_lock;        /* Lock for serialising I/O on this mutex */
-       struct mutex            validate_lock;  /* lock for validating this vnode */
+       struct rw_semaphore     validate_lock;  /* lock for validating this vnode */
        spinlock_t              wb_lock;        /* lock for wb_keys */
        spinlock_t              lock;           /* waitqueue/flags lock */
        unsigned long           flags;
@@ -519,6 +524,7 @@ struct afs_vnode {
        /* outstanding callback notification on this file */
        struct afs_cb_interest  *cb_interest;   /* Server on which this resides */
        unsigned int            cb_s_break;     /* Mass break counter on ->server */
+       unsigned int            cb_v_break;     /* Mass break counter on ->volume */
        unsigned int            cb_break;       /* Break counter on vnode */
        seqlock_t               cb_lock;        /* Lock for ->cb_interest, ->status, ->cb_*break */
 
@@ -648,16 +654,29 @@ extern void afs_init_callback_state(struct afs_server *);
 extern void afs_break_callback(struct afs_vnode *);
 extern void afs_break_callbacks(struct afs_server *, size_t, struct afs_callback_break*);
 
-extern int afs_register_server_cb_interest(struct afs_vnode *, struct afs_server_entry *);
+extern int afs_register_server_cb_interest(struct afs_vnode *,
+                                          struct afs_server_list *, unsigned int);
 extern void afs_put_cb_interest(struct afs_net *, struct afs_cb_interest *);
 extern void afs_clear_callback_interests(struct afs_net *, struct afs_server_list *);
 
 static inline struct afs_cb_interest *afs_get_cb_interest(struct afs_cb_interest *cbi)
 {
-       refcount_inc(&cbi->usage);
+       if (cbi)
+               refcount_inc(&cbi->usage);
        return cbi;
 }
 
+static inline unsigned int afs_calc_vnode_cb_break(struct afs_vnode *vnode)
+{
+       return vnode->cb_break + vnode->cb_s_break + vnode->cb_v_break;
+}
+
+static inline unsigned int afs_cb_break_sum(struct afs_vnode *vnode,
+                                           struct afs_cb_interest *cbi)
+{
+       return vnode->cb_break + cbi->server->cb_s_break + vnode->volume->cb_v_break;
+}
+
 /*
  * cell.c
  */
index ac0feac..e065bc0 100644 (file)
@@ -179,7 +179,7 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
                         */
                        if (fc->flags & AFS_FS_CURSOR_VNOVOL) {
                                fc->ac.error = -EREMOTEIO;
-                               goto failed;
+                               goto next_server;
                        }
 
                        write_lock(&vnode->volume->servers_lock);
@@ -201,7 +201,7 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
                         */
                        if (vnode->volume->servers == fc->server_list) {
                                fc->ac.error = -EREMOTEIO;
-                               goto failed;
+                               goto next_server;
                        }
 
                        /* Try again */
@@ -350,8 +350,8 @@ use_server:
         * break request before we've finished decoding the reply and
         * installing the vnode.
         */
-       fc->ac.error = afs_register_server_cb_interest(
-               vnode, &fc->server_list->servers[fc->index]);
+       fc->ac.error = afs_register_server_cb_interest(vnode, fc->server_list,
+                                                      fc->index);
        if (fc->ac.error < 0)
                goto failed;
 
@@ -369,8 +369,16 @@ use_server:
        if (!test_bit(AFS_SERVER_FL_PROBED, &server->flags)) {
                fc->ac.alist = afs_get_addrlist(alist);
 
-               if (!afs_probe_fileserver(fc))
-                       goto failed;
+               if (!afs_probe_fileserver(fc)) {
+                       switch (fc->ac.error) {
+                       case -ENOMEM:
+                       case -ERESTARTSYS:
+                       case -EINTR:
+                               goto failed;
+                       default:
+                               goto next_server;
+                       }
+               }
        }
 
        if (!fc->ac.alist)
index 5c62639..0873594 100644 (file)
@@ -41,6 +41,7 @@ int afs_open_socket(struct afs_net *net)
 {
        struct sockaddr_rxrpc srx;
        struct socket *socket;
+       unsigned int min_level;
        int ret;
 
        _enter("");
@@ -60,6 +61,12 @@ int afs_open_socket(struct afs_net *net)
        srx.transport.sin6.sin6_family  = AF_INET6;
        srx.transport.sin6.sin6_port    = htons(AFS_CM_PORT);
 
+       min_level = RXRPC_SECURITY_ENCRYPT;
+       ret = kernel_setsockopt(socket, SOL_RXRPC, RXRPC_MIN_SECURITY_LEVEL,
+                               (void *)&min_level, sizeof(min_level));
+       if (ret < 0)
+               goto error_2;
+
        ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx));
        if (ret == -EADDRINUSE) {
                srx.transport.sin6.sin6_port = 0;
@@ -482,8 +489,12 @@ static void afs_deliver_to_call(struct afs_call *call)
                state = READ_ONCE(call->state);
                switch (ret) {
                case 0:
-                       if (state == AFS_CALL_CL_PROC_REPLY)
+                       if (state == AFS_CALL_CL_PROC_REPLY) {
+                               if (call->cbi)
+                                       set_bit(AFS_SERVER_FL_MAY_HAVE_CB,
+                                               &call->cbi->server->flags);
                                goto call_complete;
+                       }
                        ASSERTCMP(state, >, AFS_CALL_CL_PROC_REPLY);
                        goto done;
                case -EINPROGRESS:
@@ -493,11 +504,6 @@ static void afs_deliver_to_call(struct afs_call *call)
                case -ECONNABORTED:
                        ASSERTCMP(state, ==, AFS_CALL_COMPLETE);
                        goto done;
-               case -ENOTCONN:
-                       abort_code = RX_CALL_DEAD;
-                       rxrpc_kernel_abort_call(call->net->socket, call->rxcall,
-                                               abort_code, ret, "KNC");
-                       goto local_abort;
                case -ENOTSUPP:
                        abort_code = RXGEN_OPCODE;
                        rxrpc_kernel_abort_call(call->net->socket, call->rxcall,
index cea2fff..1992b0f 100644 (file)
@@ -147,8 +147,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
                                        break;
                                }
 
-                               if (cb_break != (vnode->cb_break +
-                                                vnode->cb_interest->server->cb_s_break)) {
+                               if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest)) {
                                        changed = true;
                                        break;
                                }
@@ -178,7 +177,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
                }
        }
 
-       if (cb_break != (vnode->cb_break + vnode->cb_interest->server->cb_s_break))
+       if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest))
                goto someone_else_changed_it;
 
        /* We need a ref on any permits list we want to copy as we'll have to
@@ -257,7 +256,7 @@ found:
 
        spin_lock(&vnode->lock);
        zap = rcu_access_pointer(vnode->permit_cache);
-       if (cb_break == (vnode->cb_break + vnode->cb_interest->server->cb_s_break) &&
+       if (cb_break == afs_cb_break_sum(vnode, vnode->cb_interest) &&
            zap == permits)
                rcu_assign_pointer(vnode->permit_cache, replacement);
        else
index 629c749..3af4625 100644 (file)
@@ -67,12 +67,6 @@ struct afs_server *afs_find_server(struct afs_net *net,
                                                              sizeof(struct in6_addr));
                                        if (diff == 0)
                                                goto found;
-                                       if (diff < 0) {
-                                               // TODO: Sort the list
-                                               //if (i == alist->nr_ipv4)
-                                               //      goto not_found;
-                                               break;
-                                       }
                                }
                        }
                } else {
@@ -87,17 +81,10 @@ struct afs_server *afs_find_server(struct afs_net *net,
                                                        (u32 __force)b->sin6_addr.s6_addr32[3]);
                                        if (diff == 0)
                                                goto found;
-                                       if (diff < 0) {
-                                               // TODO: Sort the list
-                                               //if (i == 0)
-                                               //      goto not_found;
-                                               break;
-                                       }
                                }
                        }
                }
 
-       //not_found:
                server = NULL;
        found:
                if (server && !atomic_inc_not_zero(&server->usage))
@@ -395,14 +382,16 @@ static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
        struct afs_addr_list *alist = rcu_access_pointer(server->addresses);
        struct afs_addr_cursor ac = {
                .alist  = alist,
-               .addr   = &alist->addrs[0],
                .start  = alist->index,
-               .index  = alist->index,
+               .index  = 0,
+               .addr   = &alist->addrs[alist->index],
                .error  = 0,
        };
        _enter("%p", server);
 
-       afs_fs_give_up_all_callbacks(net, server, &ac, NULL);
+       if (test_bit(AFS_SERVER_FL_MAY_HAVE_CB, &server->flags))
+               afs_fs_give_up_all_callbacks(net, server, &ac, NULL);
+
        call_rcu(&server->rcu, afs_server_rcu);
        afs_dec_servers_outstanding(net);
 }
index 0f8dc4c..8a5760a 100644 (file)
@@ -49,6 +49,7 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
                goto error;
 
        refcount_set(&slist->usage, 1);
+       rwlock_init(&slist->lock);
 
        /* Make sure a records exists for each server in the list. */
        for (i = 0; i < vldb->nr_servers; i++) {
@@ -64,9 +65,11 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
                        goto error_2;
                }
 
-               /* Insertion-sort by server pointer */
+               /* Insertion-sort by UUID */
                for (j = 0; j < slist->nr_servers; j++)
-                       if (slist->servers[j].server >= server)
+                       if (memcmp(&slist->servers[j].server->uuid,
+                                  &server->uuid,
+                                  sizeof(server->uuid)) >= 0)
                                break;
                if (j < slist->nr_servers) {
                        if (slist->servers[j].server == server) {
index 65081ec..9e5d796 100644 (file)
@@ -590,7 +590,7 @@ static void afs_i_init_once(void *_vnode)
        memset(vnode, 0, sizeof(*vnode));
        inode_init_once(&vnode->vfs_inode);
        mutex_init(&vnode->io_lock);
-       mutex_init(&vnode->validate_lock);
+       init_rwsem(&vnode->validate_lock);
        spin_lock_init(&vnode->wb_lock);
        spin_lock_init(&vnode->lock);
        INIT_LIST_HEAD(&vnode->wb_keys);
@@ -688,7 +688,7 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
        if (afs_begin_vnode_operation(&fc, vnode, key)) {
                fc.flags |= AFS_FS_CURSOR_NO_VSLEEP;
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(vnode);
                        afs_fs_get_volume_status(&fc, &vs);
                }
 
index c164698..8b39e6e 100644 (file)
@@ -351,7 +351,7 @@ found_key:
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, vnode, wbk->key)) {
                while (afs_select_fileserver(&fc)) {
-                       fc.cb_break = vnode->cb_break + vnode->cb_s_break;
+                       fc.cb_break = afs_calc_vnode_cb_break(vnode);
                        afs_fs_store_data(&fc, mapping, first, last, offset, to);
                }
 
index 3fd4483..8c68961 100644 (file)
@@ -2436,10 +2436,8 @@ read_block_for_search(struct btrfs_root *root, struct btrfs_path *p,
        if (p->reada != READA_NONE)
                reada_for_search(fs_info, p, level, slot, key->objectid);
 
-       btrfs_release_path(p);
-
        ret = -EAGAIN;
-       tmp = read_tree_block(fs_info, blocknr, 0, parent_level - 1,
+       tmp = read_tree_block(fs_info, blocknr, gen, parent_level - 1,
                              &first_key);
        if (!IS_ERR(tmp)) {
                /*
@@ -2454,6 +2452,8 @@ read_block_for_search(struct btrfs_root *root, struct btrfs_path *p,
        } else {
                ret = PTR_ERR(tmp);
        }
+
+       btrfs_release_path(p);
        return ret;
 }
 
@@ -5414,12 +5414,24 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
        down_read(&fs_info->commit_root_sem);
        left_level = btrfs_header_level(left_root->commit_root);
        left_root_level = left_level;
-       left_path->nodes[left_level] = left_root->commit_root;
+       left_path->nodes[left_level] =
+                       btrfs_clone_extent_buffer(left_root->commit_root);
+       if (!left_path->nodes[left_level]) {
+               up_read(&fs_info->commit_root_sem);
+               ret = -ENOMEM;
+               goto out;
+       }
        extent_buffer_get(left_path->nodes[left_level]);
 
        right_level = btrfs_header_level(right_root->commit_root);
        right_root_level = right_level;
-       right_path->nodes[right_level] = right_root->commit_root;
+       right_path->nodes[right_level] =
+                       btrfs_clone_extent_buffer(right_root->commit_root);
+       if (!right_path->nodes[right_level]) {
+               up_read(&fs_info->commit_root_sem);
+               ret = -ENOMEM;
+               goto out;
+       }
        extent_buffer_get(right_path->nodes[right_level]);
        up_read(&fs_info->commit_root_sem);
 
index 2771cc5..0d422c9 100644 (file)
@@ -3182,6 +3182,8 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
                              u64 *orig_start, u64 *orig_block_len,
                              u64 *ram_bytes);
 
+void __btrfs_del_delalloc_inode(struct btrfs_root *root,
+                               struct btrfs_inode *inode);
 struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry);
 int btrfs_set_inode_index(struct btrfs_inode *dir, u64 *index);
 int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
index 60caa68..c3504b4 100644 (file)
@@ -3818,6 +3818,7 @@ void close_ctree(struct btrfs_fs_info *fs_info)
        set_bit(BTRFS_FS_CLOSING_DONE, &fs_info->flags);
 
        btrfs_free_qgroup_config(fs_info);
+       ASSERT(list_empty(&fs_info->delalloc_roots));
 
        if (percpu_counter_sum(&fs_info->delalloc_bytes)) {
                btrfs_info(fs_info, "at unmount delalloc count %lld",
@@ -4125,15 +4126,15 @@ static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info)
 
 static void btrfs_error_commit_super(struct btrfs_fs_info *fs_info)
 {
+       /* cleanup FS via transaction */
+       btrfs_cleanup_transaction(fs_info);
+
        mutex_lock(&fs_info->cleaner_mutex);
        btrfs_run_delayed_iputs(fs_info);
        mutex_unlock(&fs_info->cleaner_mutex);
 
        down_write(&fs_info->cleanup_work_sem);
        up_write(&fs_info->cleanup_work_sem);
-
-       /* cleanup FS via transaction */
-       btrfs_cleanup_transaction(fs_info);
 }
 
 static void btrfs_destroy_ordered_extents(struct btrfs_root *root)
@@ -4258,19 +4259,23 @@ static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root)
        list_splice_init(&root->delalloc_inodes, &splice);
 
        while (!list_empty(&splice)) {
+               struct inode *inode = NULL;
                btrfs_inode = list_first_entry(&splice, struct btrfs_inode,
                                               delalloc_inodes);
-
-               list_del_init(&btrfs_inode->delalloc_inodes);
-               clear_bit(BTRFS_INODE_IN_DELALLOC_LIST,
-                         &btrfs_inode->runtime_flags);
+               __btrfs_del_delalloc_inode(root, btrfs_inode);
                spin_unlock(&root->delalloc_lock);
 
-               btrfs_invalidate_inodes(btrfs_inode->root);
-
+               /*
+                * Make sure we get a live inode and that it'll not disappear
+                * meanwhile.
+                */
+               inode = igrab(&btrfs_inode->vfs_inode);
+               if (inode) {
+                       invalidate_inode_pages2(inode->i_mapping);
+                       iput(inode);
+               }
                spin_lock(&root->delalloc_lock);
        }
-
        spin_unlock(&root->delalloc_lock);
 }
 
@@ -4286,7 +4291,6 @@ static void btrfs_destroy_all_delalloc_inodes(struct btrfs_fs_info *fs_info)
        while (!list_empty(&splice)) {
                root = list_first_entry(&splice, struct btrfs_root,
                                         delalloc_root);
-               list_del_init(&root->delalloc_root);
                root = btrfs_grab_fs_root(root);
                BUG_ON(!root);
                spin_unlock(&fs_info->delalloc_root_lock);
index d241285..8e604e7 100644 (file)
@@ -1742,12 +1742,12 @@ static void btrfs_add_delalloc_inodes(struct btrfs_root *root,
        spin_unlock(&root->delalloc_lock);
 }
 
-static void btrfs_del_delalloc_inode(struct btrfs_root *root,
-                                    struct btrfs_inode *inode)
+
+void __btrfs_del_delalloc_inode(struct btrfs_root *root,
+                               struct btrfs_inode *inode)
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(inode->vfs_inode.i_sb);
 
-       spin_lock(&root->delalloc_lock);
        if (!list_empty(&inode->delalloc_inodes)) {
                list_del_init(&inode->delalloc_inodes);
                clear_bit(BTRFS_INODE_IN_DELALLOC_LIST,
@@ -1760,6 +1760,13 @@ static void btrfs_del_delalloc_inode(struct btrfs_root *root,
                        spin_unlock(&fs_info->delalloc_root_lock);
                }
        }
+}
+
+static void btrfs_del_delalloc_inode(struct btrfs_root *root,
+                                    struct btrfs_inode *inode)
+{
+       spin_lock(&root->delalloc_lock);
+       __btrfs_del_delalloc_inode(root, inode);
        spin_unlock(&root->delalloc_lock);
 }
 
index 53a8c95..dc61400 100644 (file)
@@ -380,6 +380,7 @@ static int prop_compression_apply(struct inode *inode,
                                  const char *value,
                                  size_t len)
 {
+       struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
        int type;
 
        if (len == 0) {
@@ -390,14 +391,17 @@ static int prop_compression_apply(struct inode *inode,
                return 0;
        }
 
-       if (!strncmp("lzo", value, 3))
+       if (!strncmp("lzo", value, 3)) {
                type = BTRFS_COMPRESS_LZO;
-       else if (!strncmp("zlib", value, 4))
+               btrfs_set_fs_incompat(fs_info, COMPRESS_LZO);
+       } else if (!strncmp("zlib", value, 4)) {
                type = BTRFS_COMPRESS_ZLIB;
-       else if (!strncmp("zstd", value, len))
+       } else if (!strncmp("zstd", value, len)) {
                type = BTRFS_COMPRESS_ZSTD;
-       else
+               btrfs_set_fs_incompat(fs_info, COMPRESS_ZSTD);
+       } else {
                return -EINVAL;
+       }
 
        BTRFS_I(inode)->flags &= ~BTRFS_INODE_NOCOMPRESS;
        BTRFS_I(inode)->flags |= BTRFS_INODE_COMPRESS;
index 43758e3..8f23a94 100644 (file)
@@ -4320,6 +4320,110 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
        return ret;
 }
 
+/*
+ * Log all prealloc extents beyond the inode's i_size to make sure we do not
+ * lose them after doing a fast fsync and replaying the log. We scan the
+ * subvolume's root instead of iterating the inode's extent map tree because
+ * otherwise we can log incorrect extent items based on extent map conversion.
+ * That can happen due to the fact that extent maps are merged when they
+ * are not in the extent map tree's list of modified extents.
+ */
+static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans,
+                                     struct btrfs_inode *inode,
+                                     struct btrfs_path *path)
+{
+       struct btrfs_root *root = inode->root;
+       struct btrfs_key key;
+       const u64 i_size = i_size_read(&inode->vfs_inode);
+       const u64 ino = btrfs_ino(inode);
+       struct btrfs_path *dst_path = NULL;
+       u64 last_extent = (u64)-1;
+       int ins_nr = 0;
+       int start_slot;
+       int ret;
+
+       if (!(inode->flags & BTRFS_INODE_PREALLOC))
+               return 0;
+
+       key.objectid = ino;
+       key.type = BTRFS_EXTENT_DATA_KEY;
+       key.offset = i_size;
+       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+       if (ret < 0)
+               goto out;
+
+       while (true) {
+               struct extent_buffer *leaf = path->nodes[0];
+               int slot = path->slots[0];
+
+               if (slot >= btrfs_header_nritems(leaf)) {
+                       if (ins_nr > 0) {
+                               ret = copy_items(trans, inode, dst_path, path,
+                                                &last_extent, start_slot,
+                                                ins_nr, 1, 0);
+                               if (ret < 0)
+                                       goto out;
+                               ins_nr = 0;
+                       }
+                       ret = btrfs_next_leaf(root, path);
+                       if (ret < 0)
+                               goto out;
+                       if (ret > 0) {
+                               ret = 0;
+                               break;
+                       }
+                       continue;
+               }
+
+               btrfs_item_key_to_cpu(leaf, &key, slot);
+               if (key.objectid > ino)
+                       break;
+               if (WARN_ON_ONCE(key.objectid < ino) ||
+                   key.type < BTRFS_EXTENT_DATA_KEY ||
+                   key.offset < i_size) {
+                       path->slots[0]++;
+                       continue;
+               }
+               if (last_extent == (u64)-1) {
+                       last_extent = key.offset;
+                       /*
+                        * Avoid logging extent items logged in past fsync calls
+                        * and leading to duplicate keys in the log tree.
+                        */
+                       do {
+                               ret = btrfs_truncate_inode_items(trans,
+                                                        root->log_root,
+                                                        &inode->vfs_inode,
+                                                        i_size,
+                                                        BTRFS_EXTENT_DATA_KEY);
+                       } while (ret == -EAGAIN);
+                       if (ret)
+                               goto out;
+               }
+               if (ins_nr == 0)
+                       start_slot = slot;
+               ins_nr++;
+               path->slots[0]++;
+               if (!dst_path) {
+                       dst_path = btrfs_alloc_path();
+                       if (!dst_path) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+               }
+       }
+       if (ins_nr > 0) {
+               ret = copy_items(trans, inode, dst_path, path, &last_extent,
+                                start_slot, ins_nr, 1, 0);
+               if (ret > 0)
+                       ret = 0;
+       }
+out:
+       btrfs_release_path(path);
+       btrfs_free_path(dst_path);
+       return ret;
+}
+
 static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
                                     struct btrfs_root *root,
                                     struct btrfs_inode *inode,
@@ -4362,6 +4466,11 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
                if (em->generation <= test_gen)
                        continue;
 
+               /* We log prealloc extents beyond eof later. */
+               if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags) &&
+                   em->start >= i_size_read(&inode->vfs_inode))
+                       continue;
+
                if (em->start < logged_start)
                        logged_start = em->start;
                if ((em->start + em->len - 1) > logged_end)
@@ -4374,31 +4483,6 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
                num++;
        }
 
-       /*
-        * Add all prealloc extents beyond the inode's i_size to make sure we
-        * don't lose them after doing a fast fsync and replaying the log.
-        */
-       if (inode->flags & BTRFS_INODE_PREALLOC) {
-               struct rb_node *node;
-
-               for (node = rb_last(&tree->map); node; node = rb_prev(node)) {
-                       em = rb_entry(node, struct extent_map, rb_node);
-                       if (em->start < i_size_read(&inode->vfs_inode))
-                               break;
-                       if (!list_empty(&em->list))
-                               continue;
-                       /* Same as above loop. */
-                       if (++num > 32768) {
-                               list_del_init(&tree->modified_extents);
-                               ret = -EFBIG;
-                               goto process;
-                       }
-                       refcount_inc(&em->refs);
-                       set_bit(EXTENT_FLAG_LOGGING, &em->flags);
-                       list_add_tail(&em->list, &extents);
-               }
-       }
-
        list_sort(NULL, &extents, extent_cmp);
        btrfs_get_logged_extents(inode, logged_list, logged_start, logged_end);
        /*
@@ -4443,6 +4527,9 @@ process:
        up_write(&inode->dio_sem);
 
        btrfs_release_path(path);
+       if (!ret)
+               ret = btrfs_log_prealloc_extents(trans, inode, path);
+
        return ret;
 }
 
@@ -4827,6 +4914,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
        struct extent_map_tree *em_tree = &inode->extent_tree;
        u64 logged_isize = 0;
        bool need_log_inode_item = true;
+       bool xattrs_logged = false;
 
        path = btrfs_alloc_path();
        if (!path)
@@ -5128,6 +5216,7 @@ next_key:
        err = btrfs_log_all_xattrs(trans, root, inode, path, dst_path);
        if (err)
                goto out_unlock;
+       xattrs_logged = true;
        if (max_key.type >= BTRFS_EXTENT_DATA_KEY && !fast_search) {
                btrfs_release_path(path);
                btrfs_release_path(dst_path);
@@ -5140,6 +5229,11 @@ log_extents:
        btrfs_release_path(dst_path);
        if (need_log_inode_item) {
                err = log_inode_item(trans, log, dst_path, inode);
+               if (!err && !xattrs_logged) {
+                       err = btrfs_log_all_xattrs(trans, root, inode, path,
+                                                  dst_path);
+                       btrfs_release_path(path);
+               }
                if (err)
                        goto out_unlock;
        }
index 292266f..be3fc70 100644 (file)
@@ -4052,6 +4052,15 @@ int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info)
                return 0;
        }
 
+       /*
+        * A ro->rw remount sequence should continue with the paused balance
+        * regardless of who pauses it, system or the user as of now, so set
+        * the resume flag.
+        */
+       spin_lock(&fs_info->balance_lock);
+       fs_info->balance_ctl->flags |= BTRFS_BALANCE_RESUME;
+       spin_unlock(&fs_info->balance_lock);
+
        tsk = kthread_run(balance_kthread, fs_info, "btrfs-balance");
        return PTR_ERR_OR_ZERO(tsk);
 }
index f715609..5a5a015 100644 (file)
@@ -1047,6 +1047,18 @@ out:
        return rc;
 }
 
+/*
+ * Directory operations under CIFS/SMB2/SMB3 are synchronous, so fsync()
+ * is a dummy operation.
+ */
+static int cifs_dir_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+       cifs_dbg(FYI, "Sync directory - name: %pD datasync: 0x%x\n",
+                file, datasync);
+
+       return 0;
+}
+
 static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off,
                                struct file *dst_file, loff_t destoff,
                                size_t len, unsigned int flags)
@@ -1181,6 +1193,7 @@ const struct file_operations cifs_dir_ops = {
        .copy_file_range = cifs_copy_file_range,
        .clone_file_range = cifs_clone_file_range,
        .llseek = generic_file_llseek,
+       .fsync = cifs_dir_fsync,
 };
 
 static void
index a5aa158..7a10a5d 100644 (file)
@@ -1977,14 +1977,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                goto cifs_parse_mount_err;
        }
 
-#ifdef CONFIG_CIFS_SMB_DIRECT
-       if (vol->rdma && vol->sign) {
-               cifs_dbg(VFS, "Currently SMB direct doesn't support signing."
-                       " This is being fixed\n");
-               goto cifs_parse_mount_err;
-       }
-#endif
-
 #ifndef CONFIG_KEYS
        /* Muliuser mounts require CONFIG_KEYS support */
        if (vol->multiuser) {
index b76b858..9c6d95f 100644 (file)
@@ -589,9 +589,15 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
 
        SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
 
+       /*
+        * If ea_name is NULL (listxattr) and there are no EAs, return 0 as it's
+        * not an error. Otherwise, the specified ea_name was not found.
+        */
        if (!rc)
                rc = move_smb2_ea_to_cifs(ea_data, buf_size, smb2_data,
                                          SMB2_MAX_EA_BUF, ea_name);
+       else if (!ea_name && rc == -ENODATA)
+               rc = 0;
 
        kfree(smb2_data);
        return rc;
index 60db51b..0f48741 100644 (file)
@@ -730,19 +730,14 @@ neg_exit:
 
 int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
 {
-       int rc = 0;
-       struct validate_negotiate_info_req vneg_inbuf;
+       int rc;
+       struct validate_negotiate_info_req *pneg_inbuf;
        struct validate_negotiate_info_rsp *pneg_rsp = NULL;
        u32 rsplen;
        u32 inbuflen; /* max of 4 dialects */
 
        cifs_dbg(FYI, "validate negotiate\n");
 
-#ifdef CONFIG_CIFS_SMB_DIRECT
-       if (tcon->ses->server->rdma)
-               return 0;
-#endif
-
        /* In SMB3.11 preauth integrity supersedes validate negotiate */
        if (tcon->ses->server->dialect == SMB311_PROT_ID)
                return 0;
@@ -765,63 +760,69 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
        if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
                cifs_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n");
 
-       vneg_inbuf.Capabilities =
+       pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS);
+       if (!pneg_inbuf)
+               return -ENOMEM;
+
+       pneg_inbuf->Capabilities =
                        cpu_to_le32(tcon->ses->server->vals->req_capabilities);
-       memcpy(vneg_inbuf.Guid, tcon->ses->server->client_guid,
+       memcpy(pneg_inbuf->Guid, tcon->ses->server->client_guid,
                                        SMB2_CLIENT_GUID_SIZE);
 
        if (tcon->ses->sign)
-               vneg_inbuf.SecurityMode =
+               pneg_inbuf->SecurityMode =
                        cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
        else if (global_secflags & CIFSSEC_MAY_SIGN)
-               vneg_inbuf.SecurityMode =
+               pneg_inbuf->SecurityMode =
                        cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
        else
-               vneg_inbuf.SecurityMode = 0;
+               pneg_inbuf->SecurityMode = 0;
 
 
        if (strcmp(tcon->ses->server->vals->version_string,
                SMB3ANY_VERSION_STRING) == 0) {
-               vneg_inbuf.Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
-               vneg_inbuf.Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
-               vneg_inbuf.DialectCount = cpu_to_le16(2);
+               pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
+               pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
+               pneg_inbuf->DialectCount = cpu_to_le16(2);
                /* structure is big enough for 3 dialects, sending only 2 */
-               inbuflen = sizeof(struct validate_negotiate_info_req) - 2;
+               inbuflen = sizeof(*pneg_inbuf) -
+                               sizeof(pneg_inbuf->Dialects[0]);
        } else if (strcmp(tcon->ses->server->vals->version_string,
                SMBDEFAULT_VERSION_STRING) == 0) {
-               vneg_inbuf.Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
-               vneg_inbuf.Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
-               vneg_inbuf.Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
-               vneg_inbuf.DialectCount = cpu_to_le16(3);
+               pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
+               pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
+               pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
+               pneg_inbuf->DialectCount = cpu_to_le16(3);
                /* structure is big enough for 3 dialects */
-               inbuflen = sizeof(struct validate_negotiate_info_req);
+               inbuflen = sizeof(*pneg_inbuf);
        } else {
                /* otherwise specific dialect was requested */
-               vneg_inbuf.Dialects[0] =
+               pneg_inbuf->Dialects[0] =
                        cpu_to_le16(tcon->ses->server->vals->protocol_id);
-               vneg_inbuf.DialectCount = cpu_to_le16(1);
+               pneg_inbuf->DialectCount = cpu_to_le16(1);
                /* structure is big enough for 3 dialects, sending only 1 */
-               inbuflen = sizeof(struct validate_negotiate_info_req) - 4;
+               inbuflen = sizeof(*pneg_inbuf) -
+                               sizeof(pneg_inbuf->Dialects[0]) * 2;
        }
 
        rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
                FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
-               (char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req),
-               (char **)&pneg_rsp, &rsplen);
+               (char *)pneg_inbuf, inbuflen, (char **)&pneg_rsp, &rsplen);
 
        if (rc != 0) {
                cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc);
-               return -EIO;
+               rc = -EIO;
+               goto out_free_inbuf;
        }
 
-       if (rsplen != sizeof(struct validate_negotiate_info_rsp)) {
+       rc = -EIO;
+       if (rsplen != sizeof(*pneg_rsp)) {
                cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n",
                         rsplen);
 
                /* relax check since Mac returns max bufsize allowed on ioctl */
-               if ((rsplen > CIFSMaxBufSize)
-                    || (rsplen < sizeof(struct validate_negotiate_info_rsp)))
-                       goto err_rsp_free;
+               if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp))
+                       goto out_free_rsp;
        }
 
        /* check validate negotiate info response matches what we got earlier */
@@ -838,15 +839,17 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
                goto vneg_out;
 
        /* validate negotiate successful */
+       rc = 0;
        cifs_dbg(FYI, "validate negotiate info successful\n");
-       kfree(pneg_rsp);
-       return 0;
+       goto out_free_rsp;
 
 vneg_out:
        cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n");
-err_rsp_free:
+out_free_rsp:
        kfree(pneg_rsp);
-       return -EIO;
+out_free_inbuf:
+       kfree(pneg_inbuf);
+       return rc;
 }
 
 enum securityEnum
index 183059c..30a36c2 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1706,14 +1706,13 @@ static int exec_binprm(struct linux_binprm *bprm)
 /*
  * sys_execve() executes a new program.
  */
-static int do_execveat_common(int fd, struct filename *filename,
-                             struct user_arg_ptr argv,
-                             struct user_arg_ptr envp,
-                             int flags)
+static int __do_execve_file(int fd, struct filename *filename,
+                           struct user_arg_ptr argv,
+                           struct user_arg_ptr envp,
+                           int flags, struct file *file)
 {
        char *pathbuf = NULL;
        struct linux_binprm *bprm;
-       struct file *file;
        struct files_struct *displaced;
        int retval;
 
@@ -1752,7 +1751,8 @@ static int do_execveat_common(int fd, struct filename *filename,
        check_unsafe_exec(bprm);
        current->in_execve = 1;
 
-       file = do_open_execat(fd, filename, flags);
+       if (!file)
+               file = do_open_execat(fd, filename, flags);
        retval = PTR_ERR(file);
        if (IS_ERR(file))
                goto out_unmark;
@@ -1760,7 +1760,9 @@ static int do_execveat_common(int fd, struct filename *filename,
        sched_exec();
 
        bprm->file = file;
-       if (fd == AT_FDCWD || filename->name[0] == '/') {
+       if (!filename) {
+               bprm->filename = "none";
+       } else if (fd == AT_FDCWD || filename->name[0] == '/') {
                bprm->filename = filename->name;
        } else {
                if (filename->name[0] == '\0')
@@ -1826,7 +1828,8 @@ static int do_execveat_common(int fd, struct filename *filename,
        task_numa_free(current);
        free_bprm(bprm);
        kfree(pathbuf);
-       putname(filename);
+       if (filename)
+               putname(filename);
        if (displaced)
                put_files_struct(displaced);
        return retval;
@@ -1849,10 +1852,27 @@ out_files:
        if (displaced)
                reset_files_struct(displaced);
 out_ret:
-       putname(filename);
+       if (filename)
+               putname(filename);
        return retval;
 }
 
+static int do_execveat_common(int fd, struct filename *filename,
+                             struct user_arg_ptr argv,
+                             struct user_arg_ptr envp,
+                             int flags)
+{
+       return __do_execve_file(fd, filename, argv, envp, flags, NULL);
+}
+
+int do_execve_file(struct file *file, void *__argv, void *__envp)
+{
+       struct user_arg_ptr argv = { .ptr.native = __argv };
+       struct user_arg_ptr envp = { .ptr.native = __envp };
+
+       return __do_execve_file(AT_FDCWD, NULL, argv, envp, 0, file);
+}
+
 int do_execve(struct filename *filename,
        const char __user *const __user *__argv,
        const char __user *const __user *__envp)
index 513c357..a6c0f54 100644 (file)
@@ -588,6 +588,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
        return 0;
 
 out_put_hidden_dir:
+       cancel_delayed_work_sync(&sbi->sync_work);
        iput(sbi->hidden_dir);
 out_put_root:
        dput(sb->s_root);
index 01c6b38..7869622 100644 (file)
@@ -4250,10 +4250,11 @@ out:
 static int ocfs2_reflink(struct dentry *old_dentry, struct inode *dir,
                         struct dentry *new_dentry, bool preserve)
 {
-       int error;
+       int error, had_lock;
        struct inode *inode = d_inode(old_dentry);
        struct buffer_head *old_bh = NULL;
        struct inode *new_orphan_inode = NULL;
+       struct ocfs2_lock_holder oh;
 
        if (!ocfs2_refcount_tree(OCFS2_SB(inode->i_sb)))
                return -EOPNOTSUPP;
@@ -4295,6 +4296,14 @@ static int ocfs2_reflink(struct dentry *old_dentry, struct inode *dir,
                goto out;
        }
 
+       had_lock = ocfs2_inode_lock_tracker(new_orphan_inode, NULL, 1,
+                                           &oh);
+       if (had_lock < 0) {
+               error = had_lock;
+               mlog_errno(error);
+               goto out;
+       }
+
        /* If the security isn't preserved, we need to re-initialize them. */
        if (!preserve) {
                error = ocfs2_init_security_and_acl(dir, new_orphan_inode,
@@ -4302,14 +4311,15 @@ static int ocfs2_reflink(struct dentry *old_dentry, struct inode *dir,
                if (error)
                        mlog_errno(error);
        }
-out:
        if (!error) {
                error = ocfs2_mv_orphaned_inode_to_new(dir, new_orphan_inode,
                                                       new_dentry);
                if (error)
                        mlog_errno(error);
        }
+       ocfs2_inode_unlock_tracker(new_orphan_inode, 1, &oh, had_lock);
 
+out:
        if (new_orphan_inode) {
                /*
                 * We need to open_unlock the inode no matter whether we
index 1ade120..0eaeb41 100644 (file)
@@ -43,6 +43,21 @@ config PROC_VMCORE
         help
         Exports the dump image of crashed kernel in ELF format.
 
+config PROC_VMCORE_DEVICE_DUMP
+       bool "Device Hardware/Firmware Log Collection"
+       depends on PROC_VMCORE
+       default n
+       help
+         After kernel panic, device drivers can collect the device
+         specific snapshot of their hardware or firmware before the
+         underlying devices are initialized in crash recovery kernel.
+         Note that the device driver must be present in the crash
+         recovery kernel's initramfs to collect its underlying device
+         snapshot.
+
+         If you say Y here, the collected device dumps will be added
+         as ELF notes to /proc/vmcore.
+
 config PROC_SYSCTL
        bool "Sysctl support (/proc/sys)" if EXPERT
        depends on PROC_FS
index 1b2ede6..1a76d75 100644 (file)
@@ -261,7 +261,7 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
         * Inherently racy -- command line shares address space
         * with code and data.
         */
-       rv = access_remote_vm(mm, arg_end - 1, &c, 1, 0);
+       rv = access_remote_vm(mm, arg_end - 1, &c, 1, FOLL_ANON);
        if (rv <= 0)
                goto out_free_page;
 
@@ -279,7 +279,7 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
                        int nr_read;
 
                        _count = min3(count, len, PAGE_SIZE);
-                       nr_read = access_remote_vm(mm, p, page, _count, 0);
+                       nr_read = access_remote_vm(mm, p, page, _count, FOLL_ANON);
                        if (nr_read < 0)
                                rv = nr_read;
                        if (nr_read <= 0)
@@ -325,7 +325,7 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
                                bool final;
 
                                _count = min3(count, len, PAGE_SIZE);
-                               nr_read = access_remote_vm(mm, p, page, _count, 0);
+                               nr_read = access_remote_vm(mm, p, page, _count, FOLL_ANON);
                                if (nr_read < 0)
                                        rv = nr_read;
                                if (nr_read <= 0)
@@ -946,7 +946,7 @@ static ssize_t environ_read(struct file *file, char __user *buf,
                max_len = min_t(size_t, PAGE_SIZE, count);
                this_len = min(max_len, this_len);
 
-               retval = access_remote_vm(mm, (env_start + src), page, this_len, 0);
+               retval = access_remote_vm(mm, (env_start + src), page, this_len, FOLL_ANON);
 
                if (retval <= 0) {
                        ret = retval;
index d1e8276..e64ecb9 100644 (file)
@@ -209,25 +209,34 @@ kclist_add_private(unsigned long pfn, unsigned long nr_pages, void *arg)
 {
        struct list_head *head = (struct list_head *)arg;
        struct kcore_list *ent;
+       struct page *p;
+
+       if (!pfn_valid(pfn))
+               return 1;
+
+       p = pfn_to_page(pfn);
+       if (!memmap_valid_within(pfn, p, page_zone(p)))
+               return 1;
 
        ent = kmalloc(sizeof(*ent), GFP_KERNEL);
        if (!ent)
                return -ENOMEM;
-       ent->addr = (unsigned long)__va((pfn << PAGE_SHIFT));
+       ent->addr = (unsigned long)page_to_virt(p);
        ent->size = nr_pages << PAGE_SHIFT;
 
-       /* Sanity check: Can happen in 32bit arch...maybe */
-       if (ent->addr < (unsigned long) __va(0))
+       if (!virt_addr_valid(ent->addr))
                goto free_out;
 
        /* cut not-mapped area. ....from ppc-32 code. */
        if (ULONG_MAX - ent->addr < ent->size)
                ent->size = ULONG_MAX - ent->addr;
 
-       /* cut when vmalloc() area is higher than direct-map area */
-       if (VMALLOC_START > (unsigned long)__va(0)) {
-               if (ent->addr > VMALLOC_START)
-                       goto free_out;
+       /*
+        * We've already checked virt_addr_valid so we know this address
+        * is a valid pointer, therefore we can check against it to determine
+        * if we need to trim
+        */
+       if (VMALLOC_START > ent->addr) {
                if (VMALLOC_START - ent->addr < ent->size)
                        ent->size = VMALLOC_START - ent->addr;
        }
index a45f0af..cfb6674 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/init.h>
 #include <linux/crash_dump.h>
 #include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/vmalloc.h>
 #include <linux/pagemap.h>
 #include <linux/uaccess.h>
@@ -38,12 +39,23 @@ static size_t elfcorebuf_sz_orig;
 
 static char *elfnotes_buf;
 static size_t elfnotes_sz;
+/* Size of all notes minus the device dump notes */
+static size_t elfnotes_orig_sz;
 
 /* Total size of vmcore file. */
 static u64 vmcore_size;
 
 static struct proc_dir_entry *proc_vmcore;
 
+#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
+/* Device Dump list and mutex to synchronize access to list */
+static LIST_HEAD(vmcoredd_list);
+static DEFINE_MUTEX(vmcoredd_mutex);
+#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
+
+/* Device Dump Size */
+static size_t vmcoredd_orig_sz;
+
 /*
  * Returns > 0 for RAM pages, 0 for non-RAM pages, < 0 on error
  * The called function has to take care of module refcounting.
@@ -178,6 +190,77 @@ static int copy_to(void *target, void *src, size_t size, int userbuf)
        return 0;
 }
 
+#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
+static int vmcoredd_copy_dumps(void *dst, u64 start, size_t size, int userbuf)
+{
+       struct vmcoredd_node *dump;
+       u64 offset = 0;
+       int ret = 0;
+       size_t tsz;
+       char *buf;
+
+       mutex_lock(&vmcoredd_mutex);
+       list_for_each_entry(dump, &vmcoredd_list, list) {
+               if (start < offset + dump->size) {
+                       tsz = min(offset + (u64)dump->size - start, (u64)size);
+                       buf = dump->buf + start - offset;
+                       if (copy_to(dst, buf, tsz, userbuf)) {
+                               ret = -EFAULT;
+                               goto out_unlock;
+                       }
+
+                       size -= tsz;
+                       start += tsz;
+                       dst += tsz;
+
+                       /* Leave now if buffer filled already */
+                       if (!size)
+                               goto out_unlock;
+               }
+               offset += dump->size;
+       }
+
+out_unlock:
+       mutex_unlock(&vmcoredd_mutex);
+       return ret;
+}
+
+static int vmcoredd_mmap_dumps(struct vm_area_struct *vma, unsigned long dst,
+                              u64 start, size_t size)
+{
+       struct vmcoredd_node *dump;
+       u64 offset = 0;
+       int ret = 0;
+       size_t tsz;
+       char *buf;
+
+       mutex_lock(&vmcoredd_mutex);
+       list_for_each_entry(dump, &vmcoredd_list, list) {
+               if (start < offset + dump->size) {
+                       tsz = min(offset + (u64)dump->size - start, (u64)size);
+                       buf = dump->buf + start - offset;
+                       if (remap_vmalloc_range_partial(vma, dst, buf, tsz)) {
+                               ret = -EFAULT;
+                               goto out_unlock;
+                       }
+
+                       size -= tsz;
+                       start += tsz;
+                       dst += tsz;
+
+                       /* Leave now if buffer filled already */
+                       if (!size)
+                               goto out_unlock;
+               }
+               offset += dump->size;
+       }
+
+out_unlock:
+       mutex_unlock(&vmcoredd_mutex);
+       return ret;
+}
+#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
+
 /* Read from the ELF header and then the crash dump. On error, negative value is
  * returned otherwise number of bytes read are returned.
  */
@@ -215,10 +298,41 @@ static ssize_t __read_vmcore(char *buffer, size_t buflen, loff_t *fpos,
        if (*fpos < elfcorebuf_sz + elfnotes_sz) {
                void *kaddr;
 
+               /* We add device dumps before other elf notes because the
+                * other elf notes may not fill the elf notes buffer
+                * completely and we will end up with zero-filled data
+                * between the elf notes and the device dumps. Tools will
+                * then try to decode this zero-filled data as valid notes
+                * and we don't want that. Hence, adding device dumps before
+                * the other elf notes ensure that zero-filled data can be
+                * avoided.
+                */
+#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
+               /* Read device dumps */
+               if (*fpos < elfcorebuf_sz + vmcoredd_orig_sz) {
+                       tsz = min(elfcorebuf_sz + vmcoredd_orig_sz -
+                                 (size_t)*fpos, buflen);
+                       start = *fpos - elfcorebuf_sz;
+                       if (vmcoredd_copy_dumps(buffer, start, tsz, userbuf))
+                               return -EFAULT;
+
+                       buflen -= tsz;
+                       *fpos += tsz;
+                       buffer += tsz;
+                       acc += tsz;
+
+                       /* leave now if filled buffer already */
+                       if (!buflen)
+                               return acc;
+               }
+#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
+
+               /* Read remaining elf notes */
                tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)*fpos, buflen);
-               kaddr = elfnotes_buf + *fpos - elfcorebuf_sz;
+               kaddr = elfnotes_buf + *fpos - elfcorebuf_sz - vmcoredd_orig_sz;
                if (copy_to(buffer, kaddr, tsz, userbuf))
                        return -EFAULT;
+
                buflen -= tsz;
                *fpos += tsz;
                buffer += tsz;
@@ -302,10 +416,8 @@ static const struct vm_operations_struct vmcore_mmap_ops = {
 };
 
 /**
- * alloc_elfnotes_buf - allocate buffer for ELF note segment in
- *                      vmalloc memory
- *
- * @notes_sz: size of buffer
+ * vmcore_alloc_buf - allocate buffer in vmalloc memory
+ * @sizez: size of buffer
  *
  * If CONFIG_MMU is defined, use vmalloc_user() to allow users to mmap
  * the buffer to user-space by means of remap_vmalloc_range().
@@ -313,12 +425,12 @@ static const struct vm_operations_struct vmcore_mmap_ops = {
  * If CONFIG_MMU is not defined, use vzalloc() since mmap_vmcore() is
  * disabled and there's no need to allow users to mmap the buffer.
  */
-static inline char *alloc_elfnotes_buf(size_t notes_sz)
+static inline char *vmcore_alloc_buf(size_t size)
 {
 #ifdef CONFIG_MMU
-       return vmalloc_user(notes_sz);
+       return vmalloc_user(size);
 #else
-       return vzalloc(notes_sz);
+       return vzalloc(size);
 #endif
 }
 
@@ -446,11 +558,46 @@ static int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
        if (start < elfcorebuf_sz + elfnotes_sz) {
                void *kaddr;
 
+               /* We add device dumps before other elf notes because the
+                * other elf notes may not fill the elf notes buffer
+                * completely and we will end up with zero-filled data
+                * between the elf notes and the device dumps. Tools will
+                * then try to decode this zero-filled data as valid notes
+                * and we don't want that. Hence, adding device dumps before
+                * the other elf notes ensure that zero-filled data can be
+                * avoided. This also ensures that the device dumps and
+                * other elf notes can be properly mmaped at page aligned
+                * address.
+                */
+#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
+               /* Read device dumps */
+               if (start < elfcorebuf_sz + vmcoredd_orig_sz) {
+                       u64 start_off;
+
+                       tsz = min(elfcorebuf_sz + vmcoredd_orig_sz -
+                                 (size_t)start, size);
+                       start_off = start - elfcorebuf_sz;
+                       if (vmcoredd_mmap_dumps(vma, vma->vm_start + len,
+                                               start_off, tsz))
+                               goto fail;
+
+                       size -= tsz;
+                       start += tsz;
+                       len += tsz;
+
+                       /* leave now if filled buffer already */
+                       if (!size)
+                               return 0;
+               }
+#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
+
+               /* Read remaining elf notes */
                tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)start, size);
-               kaddr = elfnotes_buf + start - elfcorebuf_sz;
+               kaddr = elfnotes_buf + start - elfcorebuf_sz - vmcoredd_orig_sz;
                if (remap_vmalloc_range_partial(vma, vma->vm_start + len,
                                                kaddr, tsz))
                        goto fail;
+
                size -= tsz;
                start += tsz;
                len += tsz;
@@ -502,8 +649,8 @@ static struct vmcore* __init get_new_element(void)
        return kzalloc(sizeof(struct vmcore), GFP_KERNEL);
 }
 
-static u64 __init get_vmcore_size(size_t elfsz, size_t elfnotesegsz,
-                                 struct list_head *vc_list)
+static u64 get_vmcore_size(size_t elfsz, size_t elfnotesegsz,
+                          struct list_head *vc_list)
 {
        u64 size;
        struct vmcore *m;
@@ -665,7 +812,7 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz,
                return rc;
 
        *notes_sz = roundup(phdr_sz, PAGE_SIZE);
-       *notes_buf = alloc_elfnotes_buf(*notes_sz);
+       *notes_buf = vmcore_alloc_buf(*notes_sz);
        if (!*notes_buf)
                return -ENOMEM;
 
@@ -698,6 +845,11 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz,
        /* Modify e_phnum to reflect merged headers. */
        ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1;
 
+       /* Store the size of all notes.  We need this to update the note
+        * header when the device dumps will be added.
+        */
+       elfnotes_orig_sz = phdr.p_memsz;
+
        return 0;
 }
 
@@ -851,7 +1003,7 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz,
                return rc;
 
        *notes_sz = roundup(phdr_sz, PAGE_SIZE);
-       *notes_buf = alloc_elfnotes_buf(*notes_sz);
+       *notes_buf = vmcore_alloc_buf(*notes_sz);
        if (!*notes_buf)
                return -ENOMEM;
 
@@ -884,6 +1036,11 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz,
        /* Modify e_phnum to reflect merged headers. */
        ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1;
 
+       /* Store the size of all notes.  We need this to update the note
+        * header when the device dumps will be added.
+        */
+       elfnotes_orig_sz = phdr.p_memsz;
+
        return 0;
 }
 
@@ -976,8 +1133,8 @@ static int __init process_ptload_program_headers_elf32(char *elfptr,
 }
 
 /* Sets offset fields of vmcore elements. */
-static void __init set_vmcore_list_offsets(size_t elfsz, size_t elfnotes_sz,
-                                          struct list_head *vc_list)
+static void set_vmcore_list_offsets(size_t elfsz, size_t elfnotes_sz,
+                                   struct list_head *vc_list)
 {
        loff_t vmcore_off;
        struct vmcore *m;
@@ -1145,6 +1302,202 @@ static int __init parse_crash_elf_headers(void)
        return 0;
 }
 
+#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
+/**
+ * vmcoredd_write_header - Write vmcore device dump header at the
+ * beginning of the dump's buffer.
+ * @buf: Output buffer where the note is written
+ * @data: Dump info
+ * @size: Size of the dump
+ *
+ * Fills beginning of the dump's buffer with vmcore device dump header.
+ */
+static void vmcoredd_write_header(void *buf, struct vmcoredd_data *data,
+                                 u32 size)
+{
+       struct vmcoredd_header *vdd_hdr = (struct vmcoredd_header *)buf;
+
+       vdd_hdr->n_namesz = sizeof(vdd_hdr->name);
+       vdd_hdr->n_descsz = size + sizeof(vdd_hdr->dump_name);
+       vdd_hdr->n_type = NT_VMCOREDD;
+
+       strncpy((char *)vdd_hdr->name, VMCOREDD_NOTE_NAME,
+               sizeof(vdd_hdr->name));
+       memcpy(vdd_hdr->dump_name, data->dump_name, sizeof(vdd_hdr->dump_name));
+}
+
+/**
+ * vmcoredd_update_program_headers - Update all Elf program headers
+ * @elfptr: Pointer to elf header
+ * @elfnotesz: Size of elf notes aligned to page size
+ * @vmcoreddsz: Size of device dumps to be added to elf note header
+ *
+ * Determine type of Elf header (Elf64 or Elf32) and update the elf note size.
+ * Also update the offsets of all the program headers after the elf note header.
+ */
+static void vmcoredd_update_program_headers(char *elfptr, size_t elfnotesz,
+                                           size_t vmcoreddsz)
+{
+       unsigned char *e_ident = (unsigned char *)elfptr;
+       u64 start, end, size;
+       loff_t vmcore_off;
+       u32 i;
+
+       vmcore_off = elfcorebuf_sz + elfnotesz;
+
+       if (e_ident[EI_CLASS] == ELFCLASS64) {
+               Elf64_Ehdr *ehdr = (Elf64_Ehdr *)elfptr;
+               Elf64_Phdr *phdr = (Elf64_Phdr *)(elfptr + sizeof(Elf64_Ehdr));
+
+               /* Update all program headers */
+               for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+                       if (phdr->p_type == PT_NOTE) {
+                               /* Update note size */
+                               phdr->p_memsz = elfnotes_orig_sz + vmcoreddsz;
+                               phdr->p_filesz = phdr->p_memsz;
+                               continue;
+                       }
+
+                       start = rounddown(phdr->p_offset, PAGE_SIZE);
+                       end = roundup(phdr->p_offset + phdr->p_memsz,
+                                     PAGE_SIZE);
+                       size = end - start;
+                       phdr->p_offset = vmcore_off + (phdr->p_offset - start);
+                       vmcore_off += size;
+               }
+       } else {
+               Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elfptr;
+               Elf32_Phdr *phdr = (Elf32_Phdr *)(elfptr + sizeof(Elf32_Ehdr));
+
+               /* Update all program headers */
+               for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+                       if (phdr->p_type == PT_NOTE) {
+                               /* Update note size */
+                               phdr->p_memsz = elfnotes_orig_sz + vmcoreddsz;
+                               phdr->p_filesz = phdr->p_memsz;
+                               continue;
+                       }
+
+                       start = rounddown(phdr->p_offset, PAGE_SIZE);
+                       end = roundup(phdr->p_offset + phdr->p_memsz,
+                                     PAGE_SIZE);
+                       size = end - start;
+                       phdr->p_offset = vmcore_off + (phdr->p_offset - start);
+                       vmcore_off += size;
+               }
+       }
+}
+
+/**
+ * vmcoredd_update_size - Update the total size of the device dumps and update
+ * Elf header
+ * @dump_size: Size of the current device dump to be added to total size
+ *
+ * Update the total size of all the device dumps and update the Elf program
+ * headers. Calculate the new offsets for the vmcore list and update the
+ * total vmcore size.
+ */
+static void vmcoredd_update_size(size_t dump_size)
+{
+       vmcoredd_orig_sz += dump_size;
+       elfnotes_sz = roundup(elfnotes_orig_sz, PAGE_SIZE) + vmcoredd_orig_sz;
+       vmcoredd_update_program_headers(elfcorebuf, elfnotes_sz,
+                                       vmcoredd_orig_sz);
+
+       /* Update vmcore list offsets */
+       set_vmcore_list_offsets(elfcorebuf_sz, elfnotes_sz, &vmcore_list);
+
+       vmcore_size = get_vmcore_size(elfcorebuf_sz, elfnotes_sz,
+                                     &vmcore_list);
+       proc_vmcore->size = vmcore_size;
+}
+
+/**
+ * vmcore_add_device_dump - Add a buffer containing device dump to vmcore
+ * @data: dump info.
+ *
+ * Allocate a buffer and invoke the calling driver's dump collect routine.
+ * Write Elf note at the beginning of the buffer to indicate vmcore device
+ * dump and add the dump to global list.
+ */
+int vmcore_add_device_dump(struct vmcoredd_data *data)
+{
+       struct vmcoredd_node *dump;
+       void *buf = NULL;
+       size_t data_size;
+       int ret;
+
+       if (!data || !strlen(data->dump_name) ||
+           !data->vmcoredd_callback || !data->size)
+               return -EINVAL;
+
+       dump = vzalloc(sizeof(*dump));
+       if (!dump) {
+               ret = -ENOMEM;
+               goto out_err;
+       }
+
+       /* Keep size of the buffer page aligned so that it can be mmaped */
+       data_size = roundup(sizeof(struct vmcoredd_header) + data->size,
+                           PAGE_SIZE);
+
+       /* Allocate buffer for driver's to write their dumps */
+       buf = vmcore_alloc_buf(data_size);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto out_err;
+       }
+
+       vmcoredd_write_header(buf, data, data_size -
+                             sizeof(struct vmcoredd_header));
+
+       /* Invoke the driver's dump collection routing */
+       ret = data->vmcoredd_callback(data, buf +
+                                     sizeof(struct vmcoredd_header));
+       if (ret)
+               goto out_err;
+
+       dump->buf = buf;
+       dump->size = data_size;
+
+       /* Add the dump to driver sysfs list */
+       mutex_lock(&vmcoredd_mutex);
+       list_add_tail(&dump->list, &vmcoredd_list);
+       mutex_unlock(&vmcoredd_mutex);
+
+       vmcoredd_update_size(data_size);
+       return 0;
+
+out_err:
+       if (buf)
+               vfree(buf);
+
+       if (dump)
+               vfree(dump);
+
+       return ret;
+}
+EXPORT_SYMBOL(vmcore_add_device_dump);
+#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
+
+/* Free all dumps in vmcore device dump list */
+static void vmcore_free_device_dumps(void)
+{
+#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
+       mutex_lock(&vmcoredd_mutex);
+       while (!list_empty(&vmcoredd_list)) {
+               struct vmcoredd_node *dump;
+
+               dump = list_first_entry(&vmcoredd_list, struct vmcoredd_node,
+                                       list);
+               list_del(&dump->list);
+               vfree(dump->buf);
+               vfree(dump);
+       }
+       mutex_unlock(&vmcoredd_mutex);
+#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
+}
+
 /* Init function for vmcore module. */
 static int __init vmcore_init(void)
 {
@@ -1192,4 +1545,7 @@ void vmcore_cleanup(void)
                kfree(m);
        }
        free_elfcorebuf();
+
+       /* clear vmcore device dump list */
+       vmcore_free_device_dumps();
 }
index b0a7f31..212b382 100644 (file)
@@ -485,7 +485,7 @@ VIRTCHNL_CHECK_STRUCT_LEN(6, virtchnl_rss_key);
 struct virtchnl_rss_lut {
        u16 vsi_id;
        u16 lut_entries;
-       u8 lut[1];        /* RSS lookup table*/
+       u8 lut[1];        /* RSS lookup table */
 };
 
 VIRTCHNL_CHECK_STRUCT_LEN(6, virtchnl_rss_lut);
@@ -819,7 +819,7 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
                return VIRTCHNL_ERR_PARAM;
        }
        /* few more checks */
-       if ((valid_len != msglen) || (err_msg_format))
+       if (err_msg_format || valid_len != msglen)
                return VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH;
 
        return 0;
index 4955e08..c05f24f 100644 (file)
@@ -150,5 +150,6 @@ extern int do_execveat(int, struct filename *,
                       const char __user * const __user *,
                       const char __user * const __user *,
                       int);
+int do_execve_file(struct file *file, void *__argv, void *__envp);
 
 #endif /* _LINUX_BINFMTS_H */
index 321969d..ed0122b 100644 (file)
@@ -627,7 +627,7 @@ bool bpf_offload_dev_match(struct bpf_prog *prog, struct bpf_map *map);
 #if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL)
 int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr);
 
-static inline bool bpf_prog_is_dev_bound(struct bpf_prog_aux *aux)
+static inline bool bpf_prog_is_dev_bound(const struct bpf_prog_aux *aux)
 {
        return aux->offload_requested;
 }
@@ -668,6 +668,7 @@ static inline void bpf_map_offload_map_free(struct bpf_map *map)
 
 #if defined(CONFIG_STREAM_PARSER) && defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_INET)
 struct sock  *__sock_map_lookup_elem(struct bpf_map *map, u32 key);
+struct sock  *__sock_hash_lookup_elem(struct bpf_map *map, void *key);
 int sock_map_prog(struct bpf_map *map, struct bpf_prog *prog, u32 type);
 #else
 static inline struct sock  *__sock_map_lookup_elem(struct bpf_map *map, u32 key)
@@ -675,6 +676,12 @@ static inline struct sock  *__sock_map_lookup_elem(struct bpf_map *map, u32 key)
        return NULL;
 }
 
+static inline struct sock  *__sock_hash_lookup_elem(struct bpf_map *map,
+                                                   void *key)
+{
+       return NULL;
+}
+
 static inline int sock_map_prog(struct bpf_map *map,
                                struct bpf_prog *prog,
                                u32 type)
@@ -724,6 +731,7 @@ extern const struct bpf_func_proto bpf_get_current_comm_proto;
 extern const struct bpf_func_proto bpf_get_stackid_proto;
 extern const struct bpf_func_proto bpf_get_stack_proto;
 extern const struct bpf_func_proto bpf_sock_map_update_proto;
+extern const struct bpf_func_proto bpf_sock_hash_update_proto;
 
 /* Shared helpers among cBPF and eBPF. */
 void bpf_user_rnd_init_once(void);
index d7df1b3..b67f879 100644 (file)
@@ -47,6 +47,7 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_HASH_OF_MAPS, htab_of_maps_map_ops)
 BPF_MAP_TYPE(BPF_MAP_TYPE_DEVMAP, dev_map_ops)
 #if defined(CONFIG_STREAM_PARSER) && defined(CONFIG_INET)
 BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKMAP, sock_map_ops)
+BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKHASH, sock_hash_ops)
 #endif
 BPF_MAP_TYPE(BPF_MAP_TYPE_CPUMAP, cpu_map_ops)
 #if defined(CONFIG_XDP_SOCKETS)
index 8f70dc1..c286813 100644 (file)
@@ -200,8 +200,8 @@ struct bpf_verifier_env {
        u32 subprog_cnt;
 };
 
-void bpf_verifier_vlog(struct bpf_verifier_log *log, const char *fmt,
-                      va_list args);
+__printf(2, 0) void bpf_verifier_vlog(struct bpf_verifier_log *log,
+                                     const char *fmt, va_list args);
 __printf(2, 3) void bpf_verifier_log_write(struct bpf_verifier_env *env,
                                           const char *fmt, ...);
 
diff --git a/include/linux/bpfilter.h b/include/linux/bpfilter.h
new file mode 100644 (file)
index 0000000..687b176
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_BPFILTER_H
+#define _LINUX_BPFILTER_H
+
+#include <uapi/linux/bpfilter.h>
+
+struct sock;
+int bpfilter_ip_set_sockopt(struct sock *sk, int optname, char *optval,
+                           unsigned int optlen);
+int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char *optval,
+                           int *optlen);
+extern int (*bpfilter_process_sockopt)(struct sock *sk, int optname,
+                                      char __user *optval,
+                                      unsigned int optlen, bool is_set);
+#endif
index a966dc6..e076c46 100644 (file)
@@ -44,5 +44,7 @@ const struct btf_type *btf_type_id_size(const struct btf *btf,
                                        u32 *ret_size);
 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);
 
 #endif
index f7ac2aa..3e4ba9d 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/kexec.h>
 #include <linux/proc_fs.h>
 #include <linux/elf.h>
+#include <uapi/linux/vmcore.h>
 
 #include <asm/pgtable.h> /* for pgprot_t */
 
@@ -93,4 +94,21 @@ static inline bool is_kdump_kernel(void) { return 0; }
 #endif /* CONFIG_CRASH_DUMP */
 
 extern unsigned long saved_max_pfn;
+
+/* Device Dump information to be filled by drivers */
+struct vmcoredd_data {
+       char dump_name[VMCOREDD_MAX_NAME_BYTES]; /* Unique name of the dump */
+       unsigned int size;                       /* Size of the dump */
+       /* Driver's registered callback to be invoked to collect dump */
+       int (*vmcoredd_callback)(struct vmcoredd_data *data, void *buf);
+};
+
+#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
+int vmcore_add_device_dump(struct vmcoredd_data *data);
+#else
+static inline int vmcore_add_device_dump(struct vmcoredd_data *data)
+{
+       return -EOPNOTSUPP;
+}
+#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
 #endif /* LINUX_CRASHDUMP_H */
index f1b7d68..3016d8c 100644 (file)
@@ -395,8 +395,8 @@ typedef struct {
        u32 attributes;
        u32 get_bar_attributes;
        u32 set_bar_attributes;
-       uint64_t romsize;
-       void *romimage;
+       u64 romsize;
+       u32 romimage;
 } efi_pci_io_protocol_32;
 
 typedef struct {
@@ -415,8 +415,8 @@ typedef struct {
        u64 attributes;
        u64 get_bar_attributes;
        u64 set_bar_attributes;
-       uint64_t romsize;
-       void *romimage;
+       u64 romsize;
+       u64 romimage;
 } efi_pci_io_protocol_64;
 
 typedef struct {
index da7e165..9dbcb9d 100644 (file)
@@ -515,9 +515,8 @@ struct sk_msg_buff {
        int sg_end;
        struct scatterlist sg_data[MAX_SKB_FRAGS];
        bool sg_copy[MAX_SKB_FRAGS];
-       __u32 key;
        __u32 flags;
-       struct bpf_map *map;
+       struct sock *sk_redir;
        struct sk_buff *skb;
        struct list_head list;
 };
index 80db19d..8de55e4 100644 (file)
@@ -28,6 +28,12 @@ struct vmcore {
        loff_t offset;
 };
 
+struct vmcoredd_node {
+       struct list_head list;  /* List of dumps */
+       void *buf;              /* Buffer containing device's dump */
+       unsigned int size;      /* Size of the buffer */
+};
+
 #ifdef CONFIG_PROC_KCORE
 extern void kclist_add(struct kcore_list *, void *, size_t, int type);
 #else
index c196176..2803264 100644 (file)
@@ -62,6 +62,7 @@ void *kthread_probe_data(struct task_struct *k);
 int kthread_park(struct task_struct *k);
 void kthread_unpark(struct task_struct *k);
 void kthread_parkme(void);
+void kthread_park_complete(struct task_struct *k);
 
 int kthreadd(void *unused);
 extern struct task_struct *kthreadd_task;
index 6930c63..6d6e79c 100644 (file)
@@ -1045,13 +1045,7 @@ static inline int mmu_notifier_retry(struct kvm *kvm, unsigned long mmu_seq)
 
 #ifdef CONFIG_HAVE_KVM_IRQ_ROUTING
 
-#ifdef CONFIG_S390
-#define KVM_MAX_IRQ_ROUTES 4096 //FIXME: we can have more than that...
-#elif defined(CONFIG_ARM64)
-#define KVM_MAX_IRQ_ROUTES 4096
-#else
-#define KVM_MAX_IRQ_ROUTES 1024
-#endif
+#define KVM_MAX_IRQ_ROUTES 4096 /* might need extension/rework in the future */
 
 bool kvm_arch_can_set_irq_routing(struct kvm *kvm);
 int kvm_set_irq_routing(struct kvm *kvm,
index 2a156c5..d703774 100644 (file)
@@ -1286,17 +1286,7 @@ enum {
 static inline const struct cpumask *
 mlx5_get_vector_affinity_hint(struct mlx5_core_dev *dev, int vector)
 {
-       struct irq_desc *desc;
-       unsigned int irq;
-       int eqn;
-       int err;
-
-       err = mlx5_vector2eqn(dev, vector, &eqn, &irq);
-       if (err)
-               return NULL;
-
-       desc = irq_to_desc(irq);
-       return desc->affinity_hint;
+       return dev->priv.irq_info[vector].mask;
 }
 
 #endif /* MLX5_DRIVER_H */
index 47aecc4..9f4d32e 100644 (file)
@@ -90,8 +90,12 @@ struct mlx5_flow_destination {
        union {
                u32                     tir_num;
                struct mlx5_flow_table  *ft;
-               u32                     vport_num;
                struct mlx5_fc          *counter;
+               struct {
+                       u16             num;
+                       u16             vhca_id;
+                       bool            vhca_id_valid;
+               } vport;
        };
 };
 
index b8918a1..b4ea8a9 100644 (file)
@@ -396,7 +396,7 @@ struct mlx5_ifc_fte_match_set_misc_bits {
        u8         reserved_at_0[0x8];
        u8         source_sqn[0x18];
 
-       u8         reserved_at_20[0x10];
+       u8         source_eswitch_owner_vhca_id[0x10];
        u8         source_port[0x10];
 
        u8         outer_second_prio[0x3];
@@ -541,7 +541,8 @@ struct mlx5_ifc_e_switch_cap_bits {
        u8         vport_svlan_insert[0x1];
        u8         vport_cvlan_insert_if_not_exist[0x1];
        u8         vport_cvlan_insert_overwrite[0x1];
-       u8         reserved_at_5[0x19];
+       u8         reserved_at_5[0x18];
+       u8         merged_eswitch[0x1];
        u8         nic_vport_node_guid_modify[0x1];
        u8         nic_vport_port_guid_modify[0x1];
 
@@ -1131,8 +1132,9 @@ enum mlx5_flow_destination_type {
 struct mlx5_ifc_dest_format_struct_bits {
        u8         destination_type[0x8];
        u8         destination_id[0x18];
-
-       u8         reserved_at_20[0x20];
+       u8         destination_eswitch_owner_vhca_id_valid[0x1];
+       u8         reserved_at_21[0xf];
+       u8         destination_eswitch_owner_vhca_id[0x10];
 };
 
 struct mlx5_ifc_flow_counter_list_bits {
@@ -6977,7 +6979,9 @@ struct mlx5_ifc_create_flow_group_in_bits {
        u8         reserved_at_a0[0x8];
        u8         table_id[0x18];
 
-       u8         reserved_at_c0[0x20];
+       u8         source_eswitch_owner_vhca_id_valid[0x1];
+
+       u8         reserved_at_c1[0x1f];
 
        u8         start_flow_index[0x20];
 
index 1ac1f06..c6fa9a2 100644 (file)
@@ -2466,6 +2466,13 @@ static inline vm_fault_t vmf_insert_pfn(struct vm_area_struct *vma,
        return VM_FAULT_NOPAGE;
 }
 
+static inline vm_fault_t vmf_error(int err)
+{
+       if (err == -ENOMEM)
+               return VM_FAULT_OOM;
+       return VM_FAULT_SIGBUS;
+}
+
 struct page *follow_page_mask(struct vm_area_struct *vma,
                              unsigned long address, unsigned int foll_flags,
                              unsigned int *page_mask);
@@ -2493,6 +2500,7 @@ static inline struct page *follow_page(struct vm_area_struct *vma,
 #define FOLL_MLOCK     0x1000  /* lock present pages */
 #define FOLL_REMOTE    0x2000  /* we are working on non-current tsk/mm */
 #define FOLL_COW       0x4000  /* internal GUP flag */
+#define FOLL_ANON      0x8000  /* don't do file mappings */
 
 static inline int vm_fault_to_errno(int vm_fault, int foll_flags)
 {
index cdd66a5..0a7abe8 100644 (file)
@@ -35,6 +35,7 @@
 #define SDIO_DEVICE_ID_BROADCOM_4335_4339      0x4335
 #define SDIO_DEVICE_ID_BROADCOM_4339           0x4339
 #define SDIO_DEVICE_ID_BROADCOM_43362          0xa962
+#define SDIO_DEVICE_ID_BROADCOM_43364          0xa9a4
 #define SDIO_DEVICE_ID_BROADCOM_43430          0xa9a6
 #define SDIO_DEVICE_ID_BROADCOM_4345           0x4345
 #define SDIO_DEVICE_ID_BROADCOM_43455          0xa9bf
index b5b43f9..01b990e 100644 (file)
@@ -312,7 +312,7 @@ void map_destroy(struct mtd_info *mtd);
 ({                                                                     \
        int i, ret = 1;                                                 \
        for (i = 0; i < map_words(map); i++) {                          \
-               if (((val1).x[i] & (val2).x[i]) != (val2).x[i]) {       \
+               if (((val1).x[i] & (val2).x[i]) != (val3).x[i]) {       \
                        ret = 0;                                        \
                        break;                                          \
                }                                                       \
index 5dad59b..17c9194 100644 (file)
@@ -867,12 +867,18 @@ struct nand_op_instr {
  * tBERS (during an erase) which all of them are u64 values that cannot be
  * divided by usual kernel macros and must be handled with the special
  * DIV_ROUND_UP_ULL() macro.
+ *
+ * Cast to type of dividend is needed here to guarantee that the result won't
+ * be an unsigned long long when the dividend is an unsigned long (or smaller),
+ * which is what the compiler does when it sees ternary operator with 2
+ * different return types (picks the largest type to make sure there's no
+ * loss).
  */
-#define __DIVIDE(dividend, divisor) ({                                 \
-       sizeof(dividend) == sizeof(u32) ?                               \
-               DIV_ROUND_UP(dividend, divisor) :                       \
-               DIV_ROUND_UP_ULL(dividend, divisor);                    \
-               })
+#define __DIVIDE(dividend, divisor) ({                                         \
+       (__typeof__(dividend))(sizeof(dividend) <= sizeof(unsigned long) ?      \
+                              DIV_ROUND_UP(dividend, divisor) :                \
+                              DIV_ROUND_UP_ULL(dividend, divisor));            \
+       })
 #define PSEC_TO_NSEC(x) __DIVIDE(x, 1000)
 #define PSEC_TO_MSEC(x) __DIVIDE(x, 1000000000)
 
index c87c3a3..623bb8c 100644 (file)
@@ -220,7 +220,6 @@ enum {
                                 NETIF_F_GSO_GRE_CSUM |                 \
                                 NETIF_F_GSO_IPXIP4 |                   \
                                 NETIF_F_GSO_IPXIP6 |                   \
-                                NETIF_F_GSO_UDP_L4 |                   \
                                 NETIF_F_GSO_UDP_TUNNEL |               \
                                 NETIF_F_GSO_UDP_TUNNEL_CSUM)
 
index 5bad038..6adac11 100644 (file)
@@ -95,6 +95,8 @@ static inline int check_stable_address_space(struct mm_struct *mm)
        return 0;
 }
 
+void __oom_reap_task_mm(struct mm_struct *mm);
+
 extern unsigned long oom_badness(struct task_struct *p,
                struct mem_cgroup *memcg, const nodemask_t *nodemask,
                unsigned long totalpages);
index b1f37a8..79b99d6 100644 (file)
@@ -133,7 +133,7 @@ static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem,
        lock_release(&sem->rw_sem.dep_map, 1, ip);
 #ifdef CONFIG_RWSEM_SPIN_ON_OWNER
        if (!read)
-               sem->rw_sem.owner = NULL;
+               sem->rw_sem.owner = RWSEM_OWNER_UNKNOWN;
 #endif
 }
 
@@ -141,6 +141,10 @@ static inline void percpu_rwsem_acquire(struct percpu_rw_semaphore *sem,
                                        bool read, unsigned long ip)
 {
        lock_acquire(&sem->rw_sem.dep_map, 0, 1, read, 1, NULL, ip);
+#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
+       if (!read)
+               sem->rw_sem.owner = current;
+#endif
 }
 
 #endif
index c9d14ee..9713aeb 100644 (file)
@@ -36,6 +36,7 @@ enum phy_mode {
        PHY_MODE_USB_DEVICE_SS,
        PHY_MODE_USB_OTG,
        PHY_MODE_SGMII,
+       PHY_MODE_2500SGMII,
        PHY_MODE_10GKR,
        PHY_MODE_UFS_HS_A,
        PHY_MODE_UFS_HS_B,
index 69d279c..8eaef2f 100644 (file)
 #define __B53_H
 
 #include <linux/kernel.h>
+#include <net/dsa.h>
 
 struct b53_platform_data {
+       /* Must be first such that dsa_register_switch() can access it */
+       struct dsa_chip_data cd;
+
        u32 chip_id;
        u16 enabled_ports;
 
diff --git a/include/linux/platform_data/mv88e6xxx.h b/include/linux/platform_data/mv88e6xxx.h
new file mode 100644 (file)
index 0000000..f63af29
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DSA_MV88E6XXX_H
+#define __DSA_MV88E6XXX_H
+
+#include <net/dsa.h>
+
+struct dsa_mv88e6xxx_pdata {
+       /* Must be first, such that dsa_register_switch() can access this
+        * without gory pointer manipulations
+        */
+       struct dsa_chip_data cd;
+       const char *compatible;
+       unsigned int enabled_ports;
+       struct net_device *netdev;
+       u32 eeprom_len;
+};
+
+#endif
index 907976f..44af652 100644 (file)
@@ -182,6 +182,272 @@ enum qed_led_mode {
        QED_LED_MODE_RESTORE
 };
 
+struct qed_mfw_tlv_eth {
+       u16 lso_maxoff_size;
+       bool lso_maxoff_size_set;
+       u16 lso_minseg_size;
+       bool lso_minseg_size_set;
+       u8 prom_mode;
+       bool prom_mode_set;
+       u16 tx_descr_size;
+       bool tx_descr_size_set;
+       u16 rx_descr_size;
+       bool rx_descr_size_set;
+       u16 netq_count;
+       bool netq_count_set;
+       u32 tcp4_offloads;
+       bool tcp4_offloads_set;
+       u32 tcp6_offloads;
+       bool tcp6_offloads_set;
+       u16 tx_descr_qdepth;
+       bool tx_descr_qdepth_set;
+       u16 rx_descr_qdepth;
+       bool rx_descr_qdepth_set;
+       u8 iov_offload;
+#define QED_MFW_TLV_IOV_OFFLOAD_NONE            (0)
+#define QED_MFW_TLV_IOV_OFFLOAD_MULTIQUEUE      (1)
+#define QED_MFW_TLV_IOV_OFFLOAD_VEB             (2)
+#define QED_MFW_TLV_IOV_OFFLOAD_VEPA            (3)
+       bool iov_offload_set;
+       u8 txqs_empty;
+       bool txqs_empty_set;
+       u8 rxqs_empty;
+       bool rxqs_empty_set;
+       u8 num_txqs_full;
+       bool num_txqs_full_set;
+       u8 num_rxqs_full;
+       bool num_rxqs_full_set;
+};
+
+#define QED_MFW_TLV_TIME_SIZE  14
+struct qed_mfw_tlv_time {
+       bool b_set;
+       u8 month;
+       u8 day;
+       u8 hour;
+       u8 min;
+       u16 msec;
+       u16 usec;
+};
+
+struct qed_mfw_tlv_fcoe {
+       u8 scsi_timeout;
+       bool scsi_timeout_set;
+       u32 rt_tov;
+       bool rt_tov_set;
+       u32 ra_tov;
+       bool ra_tov_set;
+       u32 ed_tov;
+       bool ed_tov_set;
+       u32 cr_tov;
+       bool cr_tov_set;
+       u8 boot_type;
+       bool boot_type_set;
+       u8 npiv_state;
+       bool npiv_state_set;
+       u32 num_npiv_ids;
+       bool num_npiv_ids_set;
+       u8 switch_name[8];
+       bool switch_name_set;
+       u16 switch_portnum;
+       bool switch_portnum_set;
+       u8 switch_portid[3];
+       bool switch_portid_set;
+       u8 vendor_name[8];
+       bool vendor_name_set;
+       u8 switch_model[8];
+       bool switch_model_set;
+       u8 switch_fw_version[8];
+       bool switch_fw_version_set;
+       u8 qos_pri;
+       bool qos_pri_set;
+       u8 port_alias[3];
+       bool port_alias_set;
+       u8 port_state;
+#define QED_MFW_TLV_PORT_STATE_OFFLINE  (0)
+#define QED_MFW_TLV_PORT_STATE_LOOP             (1)
+#define QED_MFW_TLV_PORT_STATE_P2P              (2)
+#define QED_MFW_TLV_PORT_STATE_FABRIC           (3)
+       bool port_state_set;
+       u16 fip_tx_descr_size;
+       bool fip_tx_descr_size_set;
+       u16 fip_rx_descr_size;
+       bool fip_rx_descr_size_set;
+       u16 link_failures;
+       bool link_failures_set;
+       u8 fcoe_boot_progress;
+       bool fcoe_boot_progress_set;
+       u64 rx_bcast;
+       bool rx_bcast_set;
+       u64 tx_bcast;
+       bool tx_bcast_set;
+       u16 fcoe_txq_depth;
+       bool fcoe_txq_depth_set;
+       u16 fcoe_rxq_depth;
+       bool fcoe_rxq_depth_set;
+       u64 fcoe_rx_frames;
+       bool fcoe_rx_frames_set;
+       u64 fcoe_rx_bytes;
+       bool fcoe_rx_bytes_set;
+       u64 fcoe_tx_frames;
+       bool fcoe_tx_frames_set;
+       u64 fcoe_tx_bytes;
+       bool fcoe_tx_bytes_set;
+       u16 crc_count;
+       bool crc_count_set;
+       u32 crc_err_src_fcid[5];
+       bool crc_err_src_fcid_set[5];
+       struct qed_mfw_tlv_time crc_err[5];
+       u16 losync_err;
+       bool losync_err_set;
+       u16 losig_err;
+       bool losig_err_set;
+       u16 primtive_err;
+       bool primtive_err_set;
+       u16 disparity_err;
+       bool disparity_err_set;
+       u16 code_violation_err;
+       bool code_violation_err_set;
+       u32 flogi_param[4];
+       bool flogi_param_set[4];
+       struct qed_mfw_tlv_time flogi_tstamp;
+       u32 flogi_acc_param[4];
+       bool flogi_acc_param_set[4];
+       struct qed_mfw_tlv_time flogi_acc_tstamp;
+       u32 flogi_rjt;
+       bool flogi_rjt_set;
+       struct qed_mfw_tlv_time flogi_rjt_tstamp;
+       u32 fdiscs;
+       bool fdiscs_set;
+       u8 fdisc_acc;
+       bool fdisc_acc_set;
+       u8 fdisc_rjt;
+       bool fdisc_rjt_set;
+       u8 plogi;
+       bool plogi_set;
+       u8 plogi_acc;
+       bool plogi_acc_set;
+       u8 plogi_rjt;
+       bool plogi_rjt_set;
+       u32 plogi_dst_fcid[5];
+       bool plogi_dst_fcid_set[5];
+       struct qed_mfw_tlv_time plogi_tstamp[5];
+       u32 plogi_acc_src_fcid[5];
+       bool plogi_acc_src_fcid_set[5];
+       struct qed_mfw_tlv_time plogi_acc_tstamp[5];
+       u8 tx_plogos;
+       bool tx_plogos_set;
+       u8 plogo_acc;
+       bool plogo_acc_set;
+       u8 plogo_rjt;
+       bool plogo_rjt_set;
+       u32 plogo_src_fcid[5];
+       bool plogo_src_fcid_set[5];
+       struct qed_mfw_tlv_time plogo_tstamp[5];
+       u8 rx_logos;
+       bool rx_logos_set;
+       u8 tx_accs;
+       bool tx_accs_set;
+       u8 tx_prlis;
+       bool tx_prlis_set;
+       u8 rx_accs;
+       bool rx_accs_set;
+       u8 tx_abts;
+       bool tx_abts_set;
+       u8 rx_abts_acc;
+       bool rx_abts_acc_set;
+       u8 rx_abts_rjt;
+       bool rx_abts_rjt_set;
+       u32 abts_dst_fcid[5];
+       bool abts_dst_fcid_set[5];
+       struct qed_mfw_tlv_time abts_tstamp[5];
+       u8 rx_rscn;
+       bool rx_rscn_set;
+       u32 rx_rscn_nport[4];
+       bool rx_rscn_nport_set[4];
+       u8 tx_lun_rst;
+       bool tx_lun_rst_set;
+       u8 abort_task_sets;
+       bool abort_task_sets_set;
+       u8 tx_tprlos;
+       bool tx_tprlos_set;
+       u8 tx_nos;
+       bool tx_nos_set;
+       u8 rx_nos;
+       bool rx_nos_set;
+       u8 ols;
+       bool ols_set;
+       u8 lr;
+       bool lr_set;
+       u8 lrr;
+       bool lrr_set;
+       u8 tx_lip;
+       bool tx_lip_set;
+       u8 rx_lip;
+       bool rx_lip_set;
+       u8 eofa;
+       bool eofa_set;
+       u8 eofni;
+       bool eofni_set;
+       u8 scsi_chks;
+       bool scsi_chks_set;
+       u8 scsi_cond_met;
+       bool scsi_cond_met_set;
+       u8 scsi_busy;
+       bool scsi_busy_set;
+       u8 scsi_inter;
+       bool scsi_inter_set;
+       u8 scsi_inter_cond_met;
+       bool scsi_inter_cond_met_set;
+       u8 scsi_rsv_conflicts;
+       bool scsi_rsv_conflicts_set;
+       u8 scsi_tsk_full;
+       bool scsi_tsk_full_set;
+       u8 scsi_aca_active;
+       bool scsi_aca_active_set;
+       u8 scsi_tsk_abort;
+       bool scsi_tsk_abort_set;
+       u32 scsi_rx_chk[5];
+       bool scsi_rx_chk_set[5];
+       struct qed_mfw_tlv_time scsi_chk_tstamp[5];
+};
+
+struct qed_mfw_tlv_iscsi {
+       u8 target_llmnr;
+       bool target_llmnr_set;
+       u8 header_digest;
+       bool header_digest_set;
+       u8 data_digest;
+       bool data_digest_set;
+       u8 auth_method;
+#define QED_MFW_TLV_AUTH_METHOD_NONE            (1)
+#define QED_MFW_TLV_AUTH_METHOD_CHAP            (2)
+#define QED_MFW_TLV_AUTH_METHOD_MUTUAL_CHAP     (3)
+       bool auth_method_set;
+       u16 boot_taget_portal;
+       bool boot_taget_portal_set;
+       u16 frame_size;
+       bool frame_size_set;
+       u16 tx_desc_size;
+       bool tx_desc_size_set;
+       u16 rx_desc_size;
+       bool rx_desc_size_set;
+       u8 boot_progress;
+       bool boot_progress_set;
+       u16 tx_desc_qdepth;
+       bool tx_desc_qdepth_set;
+       u16 rx_desc_qdepth;
+       bool rx_desc_qdepth_set;
+       u64 rx_frames;
+       bool rx_frames_set;
+       u64 rx_bytes;
+       bool rx_bytes_set;
+       u64 tx_frames;
+       bool tx_frames_set;
+       u64 tx_bytes;
+       bool tx_bytes_set;
+};
+
 #define DIRECT_REG_WR(reg_addr, val) writel((u32)val, \
                                            (void __iomem *)(reg_addr))
 
@@ -485,6 +751,14 @@ struct qed_int_info {
        u8                      used_cnt;
 };
 
+struct qed_generic_tlvs {
+#define QED_TLV_IP_CSUM         BIT(0)
+#define QED_TLV_LSO             BIT(1)
+       u16 feat_flags;
+#define QED_TLV_MAC_COUNT      3
+       u8 mac[QED_TLV_MAC_COUNT][ETH_ALEN];
+};
+
 #define QED_NVM_SIGNATURE 0x12435687
 
 enum qed_nvm_flash_cmd {
@@ -499,6 +773,8 @@ struct qed_common_cb_ops {
        void    (*link_update)(void                     *dev,
                               struct qed_link_output   *link);
        void    (*dcbx_aen)(void *dev, struct qed_dcbx_get *get, u32 mib_type);
+       void (*get_generic_tlv_data)(void *dev, struct qed_generic_tlvs *data);
+       void (*get_protocol_tlv_data)(void *dev, void *data);
 };
 
 struct qed_selftest_ops {
index 6bfd2b5..af8a61b 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <linux/compiler.h>
 #include <linux/rbtree.h>
+#include <linux/rcupdate.h>
 
 /*
  * Please note - only struct rb_augment_callbacks and the prototypes for
index ece43e8..7d012fa 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <linux/rbtree.h>
 #include <linux/seqlock.h>
+#include <linux/rcupdate.h>
 
 struct latch_tree_node {
        struct rb_node node[2];
index 56707d5..ab93b6e 100644 (file)
@@ -44,6 +44,12 @@ struct rw_semaphore {
 #endif
 };
 
+/*
+ * Setting bit 0 of the owner field with other non-zero bits will indicate
+ * that the rwsem is writer-owned with an unknown owner.
+ */
+#define RWSEM_OWNER_UNKNOWN    ((struct task_struct *)-1L)
+
 extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem);
 extern struct rw_semaphore *rwsem_down_read_failed_killable(struct rw_semaphore *sem);
 extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem);
index b3d697f..c241370 100644 (file)
@@ -112,17 +112,36 @@ struct task_group;
 
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 
+/*
+ * Special states are those that do not use the normal wait-loop pattern. See
+ * the comment with set_special_state().
+ */
+#define is_special_task_state(state)                           \
+       ((state) & (__TASK_STOPPED | __TASK_TRACED | TASK_DEAD))
+
 #define __set_current_state(state_value)                       \
        do {                                                    \
+               WARN_ON_ONCE(is_special_task_state(state_value));\
                current->task_state_change = _THIS_IP_;         \
                current->state = (state_value);                 \
        } while (0)
+
 #define set_current_state(state_value)                         \
        do {                                                    \
+               WARN_ON_ONCE(is_special_task_state(state_value));\
                current->task_state_change = _THIS_IP_;         \
                smp_store_mb(current->state, (state_value));    \
        } while (0)
 
+#define set_special_state(state_value)                                 \
+       do {                                                            \
+               unsigned long flags; /* may shadow */                   \
+               WARN_ON_ONCE(!is_special_task_state(state_value));      \
+               raw_spin_lock_irqsave(&current->pi_lock, flags);        \
+               current->task_state_change = _THIS_IP_;                 \
+               current->state = (state_value);                         \
+               raw_spin_unlock_irqrestore(&current->pi_lock, flags);   \
+       } while (0)
 #else
 /*
  * set_current_state() includes a barrier so that the write of current->state
@@ -144,8 +163,8 @@ struct task_group;
  *
  * The above is typically ordered against the wakeup, which does:
  *
- *     need_sleep = false;
- *     wake_up_state(p, TASK_UNINTERRUPTIBLE);
+ *   need_sleep = false;
+ *   wake_up_state(p, TASK_UNINTERRUPTIBLE);
  *
  * Where wake_up_state() (and all other wakeup primitives) imply enough
  * barriers to order the store of the variable against wakeup.
@@ -154,12 +173,33 @@ struct task_group;
  * once it observes the TASK_UNINTERRUPTIBLE store the waking CPU can issue a
  * TASK_RUNNING store which can collide with __set_current_state(TASK_RUNNING).
  *
- * This is obviously fine, since they both store the exact same value.
+ * However, with slightly different timing the wakeup TASK_RUNNING store can
+ * also collide with the TASK_UNINTERRUPTIBLE store. Loosing that store is not
+ * a problem either because that will result in one extra go around the loop
+ * and our @cond test will save the day.
  *
  * Also see the comments of try_to_wake_up().
  */
-#define __set_current_state(state_value) do { current->state = (state_value); } while (0)
-#define set_current_state(state_value)  smp_store_mb(current->state, (state_value))
+#define __set_current_state(state_value)                               \
+       current->state = (state_value)
+
+#define set_current_state(state_value)                                 \
+       smp_store_mb(current->state, (state_value))
+
+/*
+ * set_special_state() should be used for those states when the blocking task
+ * can not use the regular condition based wait-loop. In that case we must
+ * serialize against wakeups such that any possible in-flight TASK_RUNNING stores
+ * will not collide with our state change.
+ */
+#define set_special_state(state_value)                                 \
+       do {                                                            \
+               unsigned long flags; /* may shadow */                   \
+               raw_spin_lock_irqsave(&current->pi_lock, flags);        \
+               current->state = (state_value);                         \
+               raw_spin_unlock_irqrestore(&current->pi_lock, flags);   \
+       } while (0)
+
 #endif
 
 /* Task command name length: */
index a7ce74c..113d1ad 100644 (file)
@@ -280,7 +280,7 @@ static inline void kernel_signal_stop(void)
 {
        spin_lock_irq(&current->sighand->siglock);
        if (current->jobctl & JOBCTL_STOP_DEQUEUED)
-               __set_current_state(TASK_STOPPED);
+               set_special_state(TASK_STOPPED);
        spin_unlock_irq(&current->sighand->siglock);
 
        schedule();
index a6b6e8b..62d9b0a 100644 (file)
@@ -97,6 +97,11 @@ static inline bool skb_array_empty_any(struct skb_array *a)
        return ptr_ring_empty_any(&a->ring);
 }
 
+static inline struct sk_buff *__skb_array_consume(struct skb_array *a)
+{
+       return __ptr_ring_consume(&a->ring);
+}
+
 static inline struct sk_buff *skb_array_consume(struct skb_array *a)
 {
        return ptr_ring_consume(&a->ring);
index 8077769..72705ea 100644 (file)
@@ -218,6 +218,7 @@ struct tcp_sock {
                   reord:1;      /* reordering detected */
        } rack;
        u16     advmss;         /* Advertised MSS                       */
+       u8      compressed_ack;
        u32     chrono_start;   /* Start time in jiffies of a TCP chrono */
        u32     chrono_stat[3]; /* Time in jiffies for chrono_stat stats */
        u8      chrono_type:2,  /* current chronograph type */
@@ -297,6 +298,7 @@ struct tcp_sock {
        u32     sacked_out;     /* SACK'd packets                       */
 
        struct hrtimer  pacing_timer;
+       struct hrtimer  compressed_ack_timer;
 
        /* from STCP, retrans queue hinting */
        struct sk_buff* lost_skb_hint;
index 244aff6..5c812ac 100644 (file)
@@ -22,8 +22,10 @@ struct subprocess_info {
        const char *path;
        char **argv;
        char **envp;
+       struct file *file;
        int wait;
        int retval;
+       pid_t pid;
        int (*init)(struct subprocess_info *info, struct cred *new);
        void (*cleanup)(struct subprocess_info *info);
        void *data;
@@ -38,6 +40,16 @@ call_usermodehelper_setup(const char *path, char **argv, char **envp,
                          int (*init)(struct subprocess_info *info, struct cred *new),
                          void (*cleanup)(struct subprocess_info *), void *data);
 
+struct subprocess_info *call_usermodehelper_setup_file(struct file *file,
+                         int (*init)(struct subprocess_info *info, struct cred *new),
+                         void (*cleanup)(struct subprocess_info *), void *data);
+struct umh_info {
+       struct file *pipe_to_umh;
+       struct file *pipe_from_umh;
+       pid_t pid;
+};
+int fork_usermode_blob(void *data, size_t len, struct umh_info *info);
+
 extern int
 call_usermodehelper_exec(struct subprocess_info *info, int wait);
 
index 8312cc2..ff766ab 100644 (file)
@@ -223,6 +223,20 @@ struct ipv6_stub {
                                 const struct in6_addr *addr);
        int (*ipv6_dst_lookup)(struct net *net, struct sock *sk,
                               struct dst_entry **dst, struct flowi6 *fl6);
+
+       struct fib6_table *(*fib6_get_table)(struct net *net, u32 id);
+       struct fib6_info *(*fib6_lookup)(struct net *net, int oif,
+                                        struct flowi6 *fl6, int flags);
+       struct fib6_info *(*fib6_table_lookup)(struct net *net,
+                                             struct fib6_table *table,
+                                             int oif, struct flowi6 *fl6,
+                                             int flags);
+       struct fib6_info *(*fib6_multipath_select)(const struct net *net,
+                                                  struct fib6_info *f6i,
+                                                  struct flowi6 *fl6, int oif,
+                                                  const struct sk_buff *skb,
+                                                  int strict);
+
        void (*udpv6_encap_enable)(void);
        void (*ndisc_send_na)(struct net_device *dev, const struct in6_addr *daddr,
                              const struct in6_addr *solicited_addr,
index b619a19..893bbbb 100644 (file)
@@ -1393,6 +1393,8 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
                               const void *param, u32 timeout);
 struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
                                  const void *param, u8 event, u32 timeout);
+int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
+                  const void *param);
 
 int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
                 const void *param);
index b522351..808f1d1 100644 (file)
@@ -285,8 +285,15 @@ static inline bool bond_needs_speed_duplex(const struct bonding *bond)
 
 static inline bool bond_is_nondyn_tlb(const struct bonding *bond)
 {
-       return (BOND_MODE(bond) == BOND_MODE_TLB)  &&
-              (bond->params.tlb_dynamic_lb == 0);
+       return (bond_is_lb(bond) && bond->params.tlb_dynamic_lb == 0);
+}
+
+static inline bool bond_mode_can_use_xmit_hash(const struct bonding *bond)
+{
+       return (BOND_MODE(bond) == BOND_MODE_8023AD ||
+               BOND_MODE(bond) == BOND_MODE_XOR ||
+               BOND_MODE(bond) == BOND_MODE_TLB ||
+               BOND_MODE(bond) == BOND_MODE_ALB);
 }
 
 static inline bool bond_mode_uses_xmit_hash(const struct bonding *bond)
index 250dac3..5fbfe61 100644 (file)
@@ -1079,6 +1079,37 @@ struct sta_bss_parameters {
        u16 beacon_interval;
 };
 
+/**
+ * struct cfg80211_txq_stats - TXQ statistics for this TID
+ * @filled: bitmap of flags using the bits of &enum nl80211_txq_stats to
+ *     indicate the relevant values in this struct are filled
+ * @backlog_bytes: total number of bytes currently backlogged
+ * @backlog_packets: total number of packets currently backlogged
+ * @flows: number of new flows seen
+ * @drops: total number of packets dropped
+ * @ecn_marks: total number of packets marked with ECN CE
+ * @overlimit: number of drops due to queue space overflow
+ * @overmemory: number of drops due to memory limit overflow
+ * @collisions: number of hash collisions
+ * @tx_bytes: total number of bytes dequeued
+ * @tx_packets: total number of packets dequeued
+ * @max_flows: maximum number of flows supported
+ */
+struct cfg80211_txq_stats {
+       u32 filled;
+       u32 backlog_bytes;
+       u32 backlog_packets;
+       u32 flows;
+       u32 drops;
+       u32 ecn_marks;
+       u32 overlimit;
+       u32 overmemory;
+       u32 collisions;
+       u32 tx_bytes;
+       u32 tx_packets;
+       u32 max_flows;
+};
+
 /**
  * struct cfg80211_tid_stats - per-TID statistics
  * @filled: bitmap of flags using the bits of &enum nl80211_tid_stats to
@@ -1088,6 +1119,7 @@ struct sta_bss_parameters {
  * @tx_msdu_retries: number of retries (not counting the first) for
  *     transmitted MSDUs
  * @tx_msdu_failed: number of failed transmitted MSDUs
+ * @txq_stats: TXQ statistics
  */
 struct cfg80211_tid_stats {
        u32 filled;
@@ -1095,6 +1127,7 @@ struct cfg80211_tid_stats {
        u64 tx_msdu;
        u64 tx_msdu_retries;
        u64 tx_msdu_failed;
+       struct cfg80211_txq_stats txq_stats;
 };
 
 #define IEEE80211_MAX_CHAINS   4
@@ -1151,7 +1184,10 @@ struct cfg80211_tid_stats {
  * @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.
+ *     Note that this doesn't use the @filled bit, but is used if non-NULL.
  * @ack_signal: signal strength (in dBm) of the last ACK frame.
+ * @avg_ack_signal: average rssi value of ack packet for the no of msdu's has
+ *     been sent.
  */
 struct station_info {
        u64 filled;
@@ -1195,8 +1231,9 @@ struct station_info {
        u64 rx_beacon;
        u64 rx_duration;
        u8 rx_beacon_signal_avg;
-       struct cfg80211_tid_stats pertid[IEEE80211_NUM_TIDS + 1];
+       struct cfg80211_tid_stats *pertid;
        s8 ack_signal;
+       s8 avg_ack_signal;
 };
 
 #if IS_ENABLED(CONFIG_CFG80211)
@@ -2188,9 +2225,14 @@ struct cfg80211_connect_params {
  * have to be updated as part of update_connect_params() call.
  *
  * @UPDATE_ASSOC_IES: Indicates whether association request IEs are updated
+ * @UPDATE_FILS_ERP_INFO: Indicates that FILS connection parameters (realm,
+ *     username, erp sequence number and rrk) are updated
+ * @UPDATE_AUTH_TYPE: Indicates that authentication type is updated
  */
 enum cfg80211_connect_params_changed {
        UPDATE_ASSOC_IES                = BIT(0),
+       UPDATE_FILS_ERP_INFO            = BIT(1),
+       UPDATE_AUTH_TYPE                = BIT(2),
 };
 
 /**
@@ -2201,6 +2243,9 @@ enum cfg80211_connect_params_changed {
  * @WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed
  * @WIPHY_PARAM_COVERAGE_CLASS: coverage class changed
  * @WIPHY_PARAM_DYN_ACK: dynack has been enabled
+ * @WIPHY_PARAM_TXQ_LIMIT: TXQ packet limit has been changed
+ * @WIPHY_PARAM_TXQ_MEMORY_LIMIT: TXQ memory limit has been changed
+ * @WIPHY_PARAM_TXQ_QUANTUM: TXQ scheduler quantum
  */
 enum wiphy_params_flags {
        WIPHY_PARAM_RETRY_SHORT         = 1 << 0,
@@ -2209,6 +2254,9 @@ enum wiphy_params_flags {
        WIPHY_PARAM_RTS_THRESHOLD       = 1 << 3,
        WIPHY_PARAM_COVERAGE_CLASS      = 1 << 4,
        WIPHY_PARAM_DYN_ACK             = 1 << 5,
+       WIPHY_PARAM_TXQ_LIMIT           = 1 << 6,
+       WIPHY_PARAM_TXQ_MEMORY_LIMIT    = 1 << 7,
+       WIPHY_PARAM_TXQ_QUANTUM         = 1 << 8,
 };
 
 /**
@@ -2961,6 +3009,9 @@ struct cfg80211_external_auth_params {
  *
  * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS
  *
+ * @get_txq_stats: Get TXQ stats for interface or phy. If wdev is %NULL, this
+ *      function should return phy stats, and interface stats otherwise.
+ *
  * @set_pmk: configure the PMK to be used for offloaded 802.1X 4-Way handshake.
  *     If not deleted through @del_pmk the PMK remains valid until disconnect
  *     upon which the driver should clear it.
@@ -3262,6 +3313,10 @@ struct cfg80211_ops {
                                            struct net_device *dev,
                                            const bool enabled);
 
+       int     (*get_txq_stats)(struct wiphy *wiphy,
+                                struct wireless_dev *wdev,
+                                struct cfg80211_txq_stats *txqstats);
+
        int     (*set_pmk)(struct wiphy *wiphy, struct net_device *dev,
                           const struct cfg80211_pmk_conf *conf);
        int     (*del_pmk)(struct wiphy *wiphy, struct net_device *dev,
@@ -3806,6 +3861,10 @@ struct wiphy_iftype_ext_capab {
  *     bitmap of &enum nl80211_band values.  For instance, for
  *     NL80211_BAND_2GHZ, bit 0 would be set
  *     (i.e. BIT(NL80211_BAND_2GHZ)).
+ *
+ * @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
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -3940,6 +3999,10 @@ struct wiphy {
 
        u8 nan_supported_bands;
 
+       u32 txq_limit;
+       u32 txq_memory_limit;
+       u32 txq_quantum;
+
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
@@ -5362,6 +5425,30 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
 #define CFG80211_TESTMODE_DUMP(cmd)
 #endif
 
+/**
+ * struct cfg80211_fils_resp_params - FILS connection response params
+ * @kek: KEK derived from a successful FILS connection (may be %NULL)
+ * @kek_len: Length of @fils_kek in octets
+ * @update_erp_next_seq_num: Boolean value to specify whether the value in
+ *     @erp_next_seq_num is valid.
+ * @erp_next_seq_num: The next sequence number to use in ERP message in
+ *     FILS Authentication. This value should be specified irrespective of the
+ *     status for a FILS connection.
+ * @pmk: A new PMK if derived from a successful FILS connection (may be %NULL).
+ * @pmk_len: Length of @pmk in octets
+ * @pmkid: A new PMKID if derived from a successful FILS connection or the PMKID
+ *     used for this FILS connection (may be %NULL).
+ */
+struct cfg80211_fils_resp_params {
+       const u8 *kek;
+       size_t kek_len;
+       bool update_erp_next_seq_num;
+       u16 erp_next_seq_num;
+       const u8 *pmk;
+       size_t pmk_len;
+       const u8 *pmkid;
+};
+
 /**
  * struct cfg80211_connect_resp_params - Connection response params
  * @status: Status code, %WLAN_STATUS_SUCCESS for successful connection, use
@@ -5380,17 +5467,7 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
  * @req_ie_len: Association request IEs length
  * @resp_ie: Association response IEs (may be %NULL)
  * @resp_ie_len: Association response IEs length
- * @fils_kek: KEK derived from a successful FILS connection (may be %NULL)
- * @fils_kek_len: Length of @fils_kek in octets
- * @update_erp_next_seq_num: Boolean value to specify whether the value in
- *     @fils_erp_next_seq_num is valid.
- * @fils_erp_next_seq_num: The next sequence number to use in ERP message in
- *     FILS Authentication. This value should be specified irrespective of the
- *     status for a FILS connection.
- * @pmk: A new PMK if derived from a successful FILS connection (may be %NULL).
- * @pmk_len: Length of @pmk in octets
- * @pmkid: A new PMKID if derived from a successful FILS connection or the PMKID
- *     used for this FILS connection (may be %NULL).
+ * @fils: FILS connection response parameters.
  * @timeout_reason: Reason for connection timeout. This is used when the
  *     connection fails due to a timeout instead of an explicit rejection from
  *     the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is
@@ -5406,13 +5483,7 @@ struct cfg80211_connect_resp_params {
        size_t req_ie_len;
        const u8 *resp_ie;
        size_t resp_ie_len;
-       const u8 *fils_kek;
-       size_t fils_kek_len;
-       bool update_erp_next_seq_num;
-       u16 fils_erp_next_seq_num;
-       const u8 *pmk;
-       size_t pmk_len;
-       const u8 *pmkid;
+       struct cfg80211_fils_resp_params fils;
        enum nl80211_timeout_reason timeout_reason;
 };
 
@@ -5558,6 +5629,7 @@ cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid,
  * @req_ie_len: association request IEs length
  * @resp_ie: association response IEs (may be %NULL)
  * @resp_ie_len: assoc response IEs length
+ * @fils: FILS related roaming information.
  */
 struct cfg80211_roam_info {
        struct ieee80211_channel *channel;
@@ -5567,6 +5639,7 @@ struct cfg80211_roam_info {
        size_t req_ie_len;
        const u8 *resp_ie;
        size_t resp_ie_len;
+       struct cfg80211_fils_resp_params fils;
 };
 
 /**
@@ -5648,6 +5721,26 @@ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
                                        struct ieee80211_channel *chan,
                                        gfp_t gfp);
 
+/**
+ * cfg80211_sinfo_alloc_tid_stats - allocate per-tid statistics.
+ *
+ * @sinfo: the station information
+ * @gfp: allocation flags
+ */
+int cfg80211_sinfo_alloc_tid_stats(struct station_info *sinfo, gfp_t gfp);
+
+/**
+ * cfg80211_sinfo_release_content - release contents of station info
+ * @sinfo: the station information
+ *
+ * Releases any potentially allocated sub-information of the station
+ * information, but not the struct itself (since it's typically on
+ * the stack.)
+ */
+static inline void cfg80211_sinfo_release_content(struct station_info *sinfo)
+{
+       kfree(sinfo->pertid);
+}
 
 /**
  * cfg80211_new_sta - notify userspace about station
index 2e4f71e..9686a1a 100644 (file)
@@ -35,6 +35,14 @@ struct devlink {
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
+struct devlink_port_attrs {
+       bool set;
+       enum devlink_port_flavour flavour;
+       u32 port_number; /* same value as "split group" */
+       bool split;
+       u32 split_subport_number;
+};
+
 struct devlink_port {
        struct list_head list;
        struct devlink *devlink;
@@ -43,8 +51,7 @@ struct devlink_port {
        enum devlink_port_type type;
        enum devlink_port_type desired_type;
        void *type_dev;
-       bool split;
-       u32 split_group;
+       struct devlink_port_attrs attrs;
 };
 
 struct devlink_sb_pool_info {
@@ -367,8 +374,12 @@ void devlink_port_type_eth_set(struct devlink_port *devlink_port,
 void devlink_port_type_ib_set(struct devlink_port *devlink_port,
                              struct ib_device *ibdev);
 void devlink_port_type_clear(struct devlink_port *devlink_port);
-void devlink_port_split_set(struct devlink_port *devlink_port,
-                           u32 split_group);
+void devlink_port_attrs_set(struct devlink_port *devlink_port,
+                           enum devlink_port_flavour flavour,
+                           u32 port_number, bool split,
+                           u32 split_subport_number);
+int devlink_port_get_phys_port_name(struct devlink_port *devlink_port,
+                                   char *name, size_t len);
 int devlink_sb_register(struct devlink *devlink, unsigned int sb_index,
                        u32 size, u16 ingress_pools_count,
                        u16 egress_pools_count, u16 ingress_tc_count,
@@ -466,11 +477,20 @@ static inline void devlink_port_type_clear(struct devlink_port *devlink_port)
 {
 }
 
-static inline void devlink_port_split_set(struct devlink_port *devlink_port,
-                                         u32 split_group)
+static inline void devlink_port_attrs_set(struct devlink_port *devlink_port,
+                                         enum devlink_port_flavour flavour,
+                                         u32 port_number, bool split,
+                                         u32 split_subport_number)
 {
 }
 
+static inline int
+devlink_port_get_phys_port_name(struct devlink_port *devlink_port,
+                               char *name, size_t len)
+{
+       return -EOPNOTSUPP;
+}
+
 static inline int devlink_sb_register(struct devlink *devlink,
                                      unsigned int sb_index, u32 size,
                                      u16 ingress_pools_count,
index d044aa6..b39643e 100644 (file)
@@ -219,6 +219,33 @@ static inline __be32 erspan_get_timestamp(void)
        return htonl((u32)h_usecs);
 }
 
+/* ERSPAN BSO (Bad/Short/Oversized), see RFC1757
+ *   00b --> Good frame with no error, or unknown integrity
+ *   01b --> Payload is a Short Frame
+ *   10b --> Payload is an Oversized Frame
+ *   11b --> Payload is a Bad Frame with CRC or Alignment Error
+ */
+enum erspan_bso {
+       BSO_NOERROR = 0x0,
+       BSO_SHORT = 0x1,
+       BSO_OVERSIZED = 0x2,
+       BSO_BAD = 0x3,
+};
+
+static inline u8 erspan_detect_bso(struct sk_buff *skb)
+{
+       /* BSO_BAD is not handled because the frame CRC
+        * or alignment error information is in FCS.
+        */
+       if (skb->len < ETH_ZLEN)
+               return BSO_SHORT;
+
+       if (skb->len > ETH_FRAME_LEN)
+               return BSO_OVERSIZED;
+
+       return BSO_NOERROR;
+}
+
 static inline void erspan_build_header_v2(struct sk_buff *skb,
                                          u32 id, u8 direction, u16 hwid,
                                          bool truncate, bool is_ipv4)
@@ -248,6 +275,7 @@ static inline void erspan_build_header_v2(struct sk_buff *skb,
                vlan_tci = ntohs(qp->tci);
        }
 
+       bso = erspan_detect_bso(skb);
        skb_push(skb, sizeof(*ershdr) + ERSPAN_V2_MDSIZE);
        ershdr = (struct erspan_base_hdr *)skb->data;
        memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V2_MDSIZE);
index bada1f1..0d2281b 100644 (file)
@@ -664,4 +664,7 @@ extern int sysctl_icmp_msgs_burst;
 int ip_misc_proc_init(void);
 #endif
 
+int rtm_getroute_parse_ip_proto(struct nlattr *attr, u8 *ip_proto,
+                               struct netlink_ext_ack *extack);
+
 #endif /* _IP_H */
index a3ec08d..cc70f6d 100644 (file)
@@ -376,9 +376,24 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
                                   const struct sk_buff *skb,
                                   int flags, pol_lookup_t lookup);
 
-struct fib6_node *fib6_lookup(struct fib6_node *root,
-                             const struct in6_addr *daddr,
-                             const struct in6_addr *saddr);
+/* called with rcu lock held; can return error pointer
+ * caller needs to select path
+ */
+struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
+                             int flags);
+
+/* called with rcu lock held; caller needs to select path */
+struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table,
+                                   int oif, struct flowi6 *fl6, int strict);
+
+struct fib6_info *fib6_multipath_select(const struct net *net,
+                                       struct fib6_info *match,
+                                       struct flowi6 *fl6, int oif,
+                                       const struct sk_buff *skb, int strict);
+
+struct fib6_node *fib6_node_lookup(struct fib6_node *root,
+                                  const struct in6_addr *daddr,
+                                  const struct in6_addr *saddr);
 
 struct fib6_node *fib6_locate(struct fib6_node *root,
                              const struct in6_addr *daddr, int dst_len,
index 4cf1ef9..9e4d0f0 100644 (file)
@@ -66,12 +66,6 @@ static inline bool rt6_need_strict(const struct in6_addr *daddr)
                (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
 }
 
-static inline bool rt6_qualify_for_ecmp(const struct fib6_info *f6i)
-{
-       return (f6i->fib6_flags & (RTF_GATEWAY|RTF_ADDRCONF|RTF_DYNAMIC)) ==
-              RTF_GATEWAY;
-}
-
 void ip6_route_input(struct sk_buff *skb);
 struct dst_entry *ip6_route_input_lookup(struct net *net,
                                         struct net_device *dev,
index b2f3a0c..851a5e1 100644 (file)
@@ -3378,6 +3378,8 @@ enum ieee80211_reconfig_type {
  *     frame in case that no beacon was heard from the AP/P2P GO.
  *     The callback will be called before each transmission and upon return
  *     mac80211 will transmit the frame right away.
+ *      If duration is greater than zero, mac80211 hints to the driver the
+ *      duration for which the operation is requested.
  *     The callback is optional and can (should!) sleep.
  *
  * @mgd_protect_tdls_discover: Protect a TDLS discovery session. After sending
@@ -3697,7 +3699,8 @@ struct ieee80211_ops {
                                  u32 sset, u8 *data);
 
        void    (*mgd_prepare_tx)(struct ieee80211_hw *hw,
-                                 struct ieee80211_vif *vif);
+                                 struct ieee80211_vif *vif,
+                                 u16 duration);
 
        void    (*mgd_protect_tdls_discover)(struct ieee80211_hw *hw,
                                             struct ieee80211_vif *vif);
@@ -4449,6 +4452,19 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
  */
 u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif);
 
+/**
+ * ieee80211_csa_set_counter - request mac80211 to set csa counter
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @counter: the new value for the counter
+ *
+ * The csa counter can be changed by the device, this API should be
+ * used by the device driver to update csa counter in mac80211.
+ *
+ * It should never be used together with ieee80211_csa_update_counter(),
+ * as it will cause a race condition around the counter value.
+ */
+void ieee80211_csa_set_counter(struct ieee80211_vif *vif, u8 counter);
+
 /**
  * ieee80211_csa_finish - notify mac80211 about channel switch
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
index a94fd0c..603b514 100644 (file)
@@ -170,6 +170,7 @@ struct nft_data_desc {
 int nft_data_init(const struct nft_ctx *ctx,
                  struct nft_data *data, unsigned int size,
                  struct nft_data_desc *desc, const struct nlattr *nla);
+void nft_data_hold(const struct nft_data *data, enum nft_data_types type);
 void nft_data_release(const struct nft_data *data, enum nft_data_types type);
 int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
                  enum nft_data_types type, unsigned int len);
@@ -732,6 +733,10 @@ struct nft_expr_ops {
        int                             (*init)(const struct nft_ctx *ctx,
                                                const struct nft_expr *expr,
                                                const struct nlattr * const tb[]);
+       void                            (*activate)(const struct nft_ctx *ctx,
+                                                   const struct nft_expr *expr);
+       void                            (*deactivate)(const struct nft_ctx *ctx,
+                                                     const struct nft_expr *expr);
        void                            (*destroy)(const struct nft_ctx *ctx,
                                                   const struct nft_expr *expr);
        int                             (*dump)(struct sk_buff *skb,
index 8491bc9..661348f 100644 (file)
@@ -160,6 +160,8 @@ struct netns_ipv4 {
        int sysctl_tcp_pacing_ca_ratio;
        int sysctl_tcp_wmem[3];
        int sysctl_tcp_rmem[3];
+       int sysctl_tcp_comp_sack_nr;
+       unsigned long sysctl_tcp_comp_sack_delay_ns;
        struct inet_timewait_death_row tcp_death_row;
        int sysctl_max_syn_backlog;
        int sysctl_tcp_fastopen;
index e828d31..0005f0b 100644 (file)
@@ -683,9 +683,11 @@ static inline bool tc_skip_sw(u32 flags)
 /* SKIP_HW and SKIP_SW are mutually exclusive flags. */
 static inline bool tc_flags_valid(u32 flags)
 {
-       if (flags & ~(TCA_CLS_FLAGS_SKIP_HW | TCA_CLS_FLAGS_SKIP_SW))
+       if (flags & ~(TCA_CLS_FLAGS_SKIP_HW | TCA_CLS_FLAGS_SKIP_SW |
+                     TCA_CLS_FLAGS_VERBOSE))
                return false;
 
+       flags &= TCA_CLS_FLAGS_SKIP_HW | TCA_CLS_FLAGS_SKIP_SW;
        if (!(flags ^ (TCA_CLS_FLAGS_SKIP_HW | TCA_CLS_FLAGS_SKIP_SW)))
                return false;
 
@@ -705,7 +707,7 @@ tc_cls_common_offload_init(struct tc_cls_common_offload *cls_common,
        cls_common->chain_index = tp->chain->index;
        cls_common->protocol = tp->protocol;
        cls_common->prio = tp->prio;
-       if (tc_skip_sw(flags))
+       if (tc_skip_sw(flags) || flags & TCA_CLS_FLAGS_VERBOSE)
                cls_common->extack = extack;
 }
 
index 5154c83..98c10a2 100644 (file)
@@ -30,7 +30,6 @@ struct qdisc_rate_table {
 enum qdisc_state_t {
        __QDISC_STATE_SCHED,
        __QDISC_STATE_DEACTIVATED,
-       __QDISC_STATE_RUNNING,
 };
 
 struct qdisc_size_table {
@@ -102,6 +101,7 @@ struct Qdisc {
        refcount_t              refcnt;
 
        spinlock_t              busylock ____cacheline_aligned_in_smp;
+       spinlock_t              seqlock;
 };
 
 static inline void qdisc_refcount_inc(struct Qdisc *qdisc)
@@ -111,15 +111,21 @@ static inline void qdisc_refcount_inc(struct Qdisc *qdisc)
        refcount_inc(&qdisc->refcnt);
 }
 
-static inline bool qdisc_is_running(const struct Qdisc *qdisc)
+static inline bool qdisc_is_running(struct Qdisc *qdisc)
 {
+       if (qdisc->flags & TCQ_F_NOLOCK)
+               return spin_is_locked(&qdisc->seqlock);
        return (raw_read_seqcount(&qdisc->running) & 1) ? true : false;
 }
 
 static inline bool qdisc_run_begin(struct Qdisc *qdisc)
 {
-       if (qdisc_is_running(qdisc))
+       if (qdisc->flags & TCQ_F_NOLOCK) {
+               if (!spin_trylock(&qdisc->seqlock))
+                       return false;
+       } else if (qdisc_is_running(qdisc)) {
                return false;
+       }
        /* Variant of write_seqcount_begin() telling lockdep a trylock
         * was attempted.
         */
@@ -131,6 +137,8 @@ static inline bool qdisc_run_begin(struct Qdisc *qdisc)
 static inline void qdisc_run_end(struct Qdisc *qdisc)
 {
        write_seqcount_end(&qdisc->running);
+       if (qdisc->flags & TCQ_F_NOLOCK)
+               spin_unlock(&qdisc->seqlock);
 }
 
 static inline bool qdisc_may_bulk(const struct Qdisc *qdisc)
index 3b1d617..952d842 100644 (file)
@@ -245,6 +245,7 @@ extern long sysctl_tcp_mem[3];
 
 #define TCP_RACK_LOSS_DETECTION  0x1 /* Use RACK to detect losses */
 #define TCP_RACK_STATIC_REO_WND  0x2 /* Use static RACK reo wnd */
+#define TCP_RACK_NO_DUPTHRESH    0x4 /* Do not use DUPACK threshold in RACK */
 
 extern atomic_long_t tcp_memory_allocated;
 extern struct percpu_counter tcp_sockets_allocated;
@@ -558,7 +559,10 @@ void tcp_init_xmit_timers(struct sock *);
 static inline void tcp_clear_xmit_timers(struct sock *sk)
 {
        if (hrtimer_try_to_cancel(&tcp_sk(sk)->pacing_timer) == 1)
-               sock_put(sk);
+               __sock_put(sk);
+
+       if (hrtimer_try_to_cancel(&tcp_sk(sk)->compressed_ack_timer) == 1)
+               __sock_put(sk);
 
        inet_csk_clear_xmit_timers(sk);
 }
@@ -816,9 +820,8 @@ struct tcp_skb_cb {
 #endif
                } header;       /* For incoming skbs */
                struct {
-                       __u32 key;
                        __u32 flags;
-                       struct bpf_map *map;
+                       struct sock *sk_redir;
                        void *data_end;
                } bpf;
        };
@@ -1877,6 +1880,10 @@ void tcp_v4_init(void);
 void tcp_init(void);
 
 /* tcp_recovery.c */
+void tcp_mark_skb_lost(struct sock *sk, struct sk_buff *skb);
+void tcp_newreno_mark_lost(struct sock *sk, bool snd_una_advanced);
+extern s32 tcp_rack_skb_timeout(struct tcp_sock *tp, struct sk_buff *skb,
+                               u32 reo_wnd);
 extern void tcp_rack_mark_lost(struct sock *sk);
 extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq,
                             u64 xmit_time);
index ee78f33..70c2737 100644 (file)
@@ -114,6 +114,10 @@ struct tls_sw_context_rx {
        struct sk_buff *recv_pkt;
        u8 control;
        bool decrypted;
+
+       char rx_aad_ciphertext[TLS_AAD_SPACE_SIZE];
+       char rx_aad_plaintext[TLS_AAD_SPACE_SIZE];
+
 };
 
 struct tls_record_info {
index f082055..d0a341b 100644 (file)
@@ -575,6 +575,48 @@ TRACE_EVENT(afs_protocol_error,
                      __entry->call, __entry->error, __entry->where)
            );
 
+TRACE_EVENT(afs_cm_no_server,
+           TP_PROTO(struct afs_call *call, struct sockaddr_rxrpc *srx),
+
+           TP_ARGS(call, srx),
+
+           TP_STRUCT__entry(
+                   __field(unsigned int,                       call    )
+                   __field(unsigned int,                       op_id   )
+                   __field_struct(struct sockaddr_rxrpc,       srx     )
+                            ),
+
+           TP_fast_assign(
+                   __entry->call = call->debug_id;
+                   __entry->op_id = call->operation_ID;
+                   memcpy(&__entry->srx, srx, sizeof(__entry->srx));
+                          ),
+
+           TP_printk("c=%08x op=%u %pISpc",
+                     __entry->call, __entry->op_id, &__entry->srx.transport)
+           );
+
+TRACE_EVENT(afs_cm_no_server_u,
+           TP_PROTO(struct afs_call *call, const uuid_t *uuid),
+
+           TP_ARGS(call, uuid),
+
+           TP_STRUCT__entry(
+                   __field(unsigned int,                       call    )
+                   __field(unsigned int,                       op_id   )
+                   __field_struct(uuid_t,                      uuid    )
+                            ),
+
+           TP_fast_assign(
+                   __entry->call = call->debug_id;
+                   __entry->op_id = call->operation_ID;
+                   memcpy(&__entry->uuid, uuid, sizeof(__entry->uuid));
+                          ),
+
+           TP_printk("c=%08x op=%u %pU",
+                     __entry->call, __entry->op_id, &__entry->uuid)
+           );
+
 #endif /* _TRACE_AFS_H */
 
 /* This part must be outside protection */
index 7e8d48a..1b8d951 100644 (file)
 
 TRACE_EVENT(fib6_table_lookup,
 
-       TP_PROTO(const struct net *net, const struct rt6_info *rt,
+       TP_PROTO(const struct net *net, const struct fib6_info *f6i,
                 struct fib6_table *table, const struct flowi6 *flp),
 
-       TP_ARGS(net, rt, table, flp),
+       TP_ARGS(net, f6i, table, flp),
 
        TP_STRUCT__entry(
                __field(        u32,    tb_id           )
@@ -48,20 +48,20 @@ TRACE_EVENT(fib6_table_lookup,
                in6 = (struct in6_addr *)__entry->dst;
                *in6 = flp->daddr;
 
-               if (rt->rt6i_idev) {
-                       __assign_str(name, rt->rt6i_idev->dev->name);
+               if (f6i->fib6_nh.nh_dev) {
+                       __assign_str(name, f6i->fib6_nh.nh_dev);
                } else {
                        __assign_str(name, "");
                }
-               if (rt == net->ipv6.ip6_null_entry) {
+               if (f6i == net->ipv6.fib6_null_entry) {
                        struct in6_addr in6_zero = {};
 
                        in6 = (struct in6_addr *)__entry->gw;
                        *in6 = in6_zero;
 
-               } else if (rt) {
+               } else if (f6i) {
                        in6 = (struct in6_addr *)__entry->gw;
-                       *in6 = rt->rt6i_gateway;
+                       *in6 = f6i->fib6_nh.nh_gw;
                }
        ),
 
index 7dd8f34..fdcf88b 100644 (file)
@@ -352,22 +352,6 @@ DECLARE_EVENT_CLASS(xen_mmu_pgd,
 DEFINE_XEN_MMU_PGD_EVENT(xen_mmu_pgd_pin);
 DEFINE_XEN_MMU_PGD_EVENT(xen_mmu_pgd_unpin);
 
-TRACE_EVENT(xen_mmu_flush_tlb_all,
-           TP_PROTO(int x),
-           TP_ARGS(x),
-           TP_STRUCT__entry(__array(char, x, 0)),
-           TP_fast_assign((void)x),
-           TP_printk("%s", "")
-       );
-
-TRACE_EVENT(xen_mmu_flush_tlb,
-           TP_PROTO(int x),
-           TP_ARGS(x),
-           TP_STRUCT__entry(__array(char, x, 0)),
-           TP_fast_assign((void)x),
-           TP_printk("%s", "")
-       );
-
 TRACE_EVENT(xen_mmu_flush_tlb_one_user,
            TP_PROTO(unsigned long addr),
            TP_ARGS(addr),
index 93d5a4e..d94d333 100644 (file)
@@ -96,6 +96,7 @@ enum bpf_cmd {
        BPF_PROG_QUERY,
        BPF_RAW_TRACEPOINT_OPEN,
        BPF_BTF_LOAD,
+       BPF_BTF_GET_FD_BY_ID,
 };
 
 enum bpf_map_type {
@@ -117,6 +118,7 @@ enum bpf_map_type {
        BPF_MAP_TYPE_SOCKMAP,
        BPF_MAP_TYPE_CPUMAP,
        BPF_MAP_TYPE_XSKMAP,
+       BPF_MAP_TYPE_SOCKHASH,
 };
 
 enum bpf_prog_type {
@@ -344,6 +346,7 @@ union bpf_attr {
                        __u32           start_id;
                        __u32           prog_id;
                        __u32           map_id;
+                       __u32           btf_id;
                };
                __u32           next_id;
                __u32           open_flags;
@@ -1826,6 +1829,79 @@ union bpf_attr {
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
+ * int bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags)
+ *     Description
+ *             Do FIB lookup in kernel tables using parameters in *params*.
+ *             If lookup is successful and result shows packet is to be
+ *             forwarded, the neighbor tables are searched for the nexthop.
+ *             If successful (ie., FIB lookup shows forwarding and nexthop
+ *             is resolved), the nexthop address is returned in ipv4_dst,
+ *             ipv6_dst or mpls_out based on family, smac is set to mac
+ *             address of egress device, dmac is set to nexthop mac address,
+ *             rt_metric is set to metric from route.
+ *
+ *             *plen* argument is the size of the passed in struct.
+ *             *flags* argument can be one or more BPF_FIB_LOOKUP_ flags:
+ *
+ *             **BPF_FIB_LOOKUP_DIRECT** means do a direct table lookup vs
+ *             full lookup using FIB rules
+ *             **BPF_FIB_LOOKUP_OUTPUT** means do 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
+ *             Egress device index on success, 0 if packet needs to continue
+ *             up the stack for further processing or a negative error in case
+ *             of failure.
+ *
+ * int bpf_sock_hash_update(struct bpf_sock_ops_kern *skops, struct bpf_map *map, void *key, u64 flags)
+ *     Description
+ *             Add an entry to, or update a sockhash *map* referencing sockets.
+ *             The *skops* is used as a new value for the entry associated to
+ *             *key*. *flags* is one of:
+ *
+ *             **BPF_NOEXIST**
+ *                     The entry for *key* must not exist in the map.
+ *             **BPF_EXIST**
+ *                     The entry for *key* must already exist in the map.
+ *             **BPF_ANY**
+ *                     No condition on the existence of the entry for *key*.
+ *
+ *             If the *map* has eBPF programs (parser and verdict), those will
+ *             be inherited by the socket being added. If the socket is
+ *             already attached to eBPF programs, this results in an error.
+ *     Return
+ *             0 on success, or a negative error in case of failure.
+ *
+ * int bpf_msg_redirect_hash(struct sk_msg_buff *msg, struct bpf_map *map, void *key, u64 flags)
+ *     Description
+ *             This helper is used in programs implementing policies at the
+ *             socket level. If the message *msg* is allowed to pass (i.e. if
+ *             the verdict eBPF program returns **SK_PASS**), redirect it to
+ *             the socket referenced by *map* (of type
+ *             **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
+ *             egress interfaces can be used for redirection. The
+ *             **BPF_F_INGRESS** value in *flags* is used to make the
+ *             distinction (ingress path is selected if the flag is present,
+ *             egress path otherwise). This is the only flag supported for now.
+ *     Return
+ *             **SK_PASS** on success, or **SK_DROP** on error.
+ *
+ * int bpf_sk_redirect_hash(struct sk_buff *skb, struct bpf_map *map, void *key, u64 flags)
+ *     Description
+ *             This helper is used in programs implementing policies at the
+ *             skb socket level. If the sk_buff *skb* is allowed to pass (i.e.
+ *             if the verdeict eBPF program returns **SK_PASS**), redirect it
+ *             to the socket referenced by *map* (of type
+ *             **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
+ *             egress interfaces can be used for redirection. The
+ *             **BPF_F_INGRESS** value in *flags* is used to make the
+ *             distinction (ingress path is selected if the flag is present,
+ *             egress otherwise). This is the only flag supported for now.
+ *     Return
+ *             **SK_PASS** on success, or **SK_DROP** on error.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -1896,7 +1972,11 @@ union bpf_attr {
        FN(xdp_adjust_tail),            \
        FN(skb_get_xfrm_state),         \
        FN(get_stack),                  \
-       FN(skb_load_bytes_relative),
+       FN(skb_load_bytes_relative),    \
+       FN(fib_lookup),                 \
+       FN(sock_hash_update),           \
+       FN(msg_redirect_hash),          \
+       FN(sk_redirect_hash),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -2130,6 +2210,15 @@ struct bpf_map_info {
        __u32 ifindex;
        __u64 netns_dev;
        __u64 netns_ino;
+       __u32 btf_id;
+       __u32 btf_key_id;
+       __u32 btf_value_id;
+} __attribute__((aligned(8)));
+
+struct bpf_btf_info {
+       __aligned_u64 btf;
+       __u32 btf_size;
+       __u32 id;
 } __attribute__((aligned(8)));
 
 /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
@@ -2310,4 +2399,55 @@ struct bpf_raw_tracepoint_args {
        __u64 args[0];
 };
 
+/* DIRECT:  Skip the FIB rules and go to FIB table associated with device
+ * OUTPUT:  Do lookup from egress perspective; default is ingress
+ */
+#define BPF_FIB_LOOKUP_DIRECT  BIT(0)
+#define BPF_FIB_LOOKUP_OUTPUT  BIT(1)
+
+struct bpf_fib_lookup {
+       /* input */
+       __u8    family;   /* network family, AF_INET, AF_INET6, AF_MPLS */
+
+       /* set if lookup is to consider L4 data - e.g., FIB rules */
+       __u8    l4_protocol;
+       __be16  sport;
+       __be16  dport;
+
+       /* total length of packet from network header - used for MTU check */
+       __u16   tot_len;
+       __u32   ifindex;  /* L3 device index for lookup */
+
+       union {
+               /* inputs to lookup */
+               __u8    tos;            /* AF_INET  */
+               __be32  flowlabel;      /* AF_INET6 */
+
+               /* output: metric of fib result */
+               __u32 rt_metric;
+       };
+
+       union {
+               __be32          mpls_in;
+               __be32          ipv4_src;
+               __u32           ipv6_src[4];  /* in6_addr; network order */
+       };
+
+       /* input to bpf_fib_lookup, *dst is destination address.
+        * output: bpf_fib_lookup sets to gateway address
+        */
+       union {
+               /* return for MPLS lookups */
+               __be32          mpls_out[4];  /* support up to 4 labels */
+               __be32          ipv4_dst;
+               __u32           ipv6_dst[4];  /* in6_addr; network order */
+       };
+
+       /* output */
+       __be16  h_vlan_proto;
+       __be16  h_vlan_TCI;
+       __u8    smac[6];     /* ETH_ALEN */
+       __u8    dmac[6];     /* ETH_ALEN */
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/include/uapi/linux/bpfilter.h b/include/uapi/linux/bpfilter.h
new file mode 100644 (file)
index 0000000..2ec3cc9
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _UAPI_LINUX_BPFILTER_H
+#define _UAPI_LINUX_BPFILTER_H
+
+#include <linux/if.h>
+
+enum {
+       BPFILTER_IPT_SO_SET_REPLACE = 64,
+       BPFILTER_IPT_SO_SET_ADD_COUNTERS = 65,
+       BPFILTER_IPT_SET_MAX,
+};
+
+enum {
+       BPFILTER_IPT_SO_GET_INFO = 64,
+       BPFILTER_IPT_SO_GET_ENTRIES = 65,
+       BPFILTER_IPT_SO_GET_REVISION_MATCH = 66,
+       BPFILTER_IPT_SO_GET_REVISION_TARGET = 67,
+       BPFILTER_IPT_GET_MAX,
+};
+
+#endif /* _UAPI_LINUX_BPFILTER_H */
index 1df65a4..75cb545 100644 (file)
@@ -132,6 +132,16 @@ enum devlink_eswitch_encap_mode {
        DEVLINK_ESWITCH_ENCAP_MODE_BASIC,
 };
 
+enum devlink_port_flavour {
+       DEVLINK_PORT_FLAVOUR_PHYSICAL, /* Any kind of a port physically
+                                       * facing the user.
+                                       */
+       DEVLINK_PORT_FLAVOUR_CPU, /* CPU port */
+       DEVLINK_PORT_FLAVOUR_DSA, /* Distributed switch architecture
+                                  * interconnect port.
+                                  */
+};
+
 enum devlink_attr {
        /* don't change the order or add anything between, this is ABI! */
        DEVLINK_ATTR_UNSPEC,
@@ -224,6 +234,10 @@ enum devlink_attr {
        DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID,   /* u64 */
        DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS,/* u64 */
 
+       DEVLINK_ATTR_PORT_FLAVOUR,              /* u16 */
+       DEVLINK_ATTR_PORT_NUMBER,               /* u32 */
+       DEVLINK_ATTR_PORT_SPLIT_SUBPORT_NUMBER, /* u32 */
+
        /* add new attributes above here, update the policy in devlink.c */
 
        __DEVLINK_ATTR_MAX,
index e2535d6..4e12c42 100644 (file)
@@ -421,6 +421,7 @@ typedef struct elf64_shdr {
 #define NT_ARM_SYSTEM_CALL     0x404   /* ARM system call number */
 #define NT_ARM_SVE     0x405           /* ARM Scalable Vector Extension registers */
 #define NT_ARC_V2      0x600           /* ARCv2 accumulator/extra registers */
+#define NT_VMCOREDD    0x700           /* Vmcore Device Dump Note */
 
 /* Note header in a PT_NOTE section */
 typedef struct elf32_note {
index 74b9115..bcba72d 100644 (file)
@@ -46,6 +46,9 @@ enum tcp_conntrack {
 /* Marks possibility for expected RFC5961 challenge ACK */
 #define IP_CT_EXP_CHALLENGE_ACK                0x40
 
+/* Simultaneous open initialized */
+#define IP_CT_TCP_SIMULTANEOUS_OPEN            0x80
+
 struct nf_ct_tcp_flags {
        __u8 flags;
        __u8 mask;
index 9c36301..06f9af2 100644 (file)
@@ -11,6 +11,7 @@
  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
  * Copyright 2008 Colin McCabe <colin@cozybit.com>
  * Copyright 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * FILS shared key authentication offload should be able to construct the
  * authentication and association frames for FILS shared key authentication and
  * eventually do a key derivation as per IEEE 802.11ai. The below additional
- * parameters should be given to driver in %NL80211_CMD_CONNECT.
+ * parameters should be given to driver in %NL80211_CMD_CONNECT and/or in
+ * %NL80211_CMD_UPDATE_CONNECT_PARAMS.
  *     %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai
  *     %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai
  *     %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message
  * as specified in IETF RFC 6696.
  *
  * When FILS shared key authentication is completed, driver needs to provide the
- * below additional parameters to userspace.
+ * below additional parameters to userspace, which can be either after setting
+ * up a connection or after roaming.
  *     %NL80211_ATTR_FILS_KEK - used for key renewal
  *     %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges
  *     %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated
@@ -2225,6 +2228,16 @@ enum nl80211_commands {
  * @NL80211_ATTR_NSS: Station's New/updated  RX_NSS value notified using this
  *     u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
  *
+ * @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum
+ *      nl80211_txq_stats)
+ * @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy.
+ *      The smaller of this and the memory limit is enforced.
+ * @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory memory limit (in bytes) for the
+ *      TXQ queues for this phy. The smaller of this and the packet limit is
+ *      enforced.
+ * @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes
+ *      a flow is assigned on each round of the DRR scheduler.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2659,6 +2672,11 @@ enum nl80211_attrs {
 
        NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
 
+       NL80211_ATTR_TXQ_STATS,
+       NL80211_ATTR_TXQ_LIMIT,
+       NL80211_ATTR_TXQ_MEMORY_LIMIT,
+       NL80211_ATTR_TXQ_QUANTUM,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2982,6 +3000,8 @@ enum nl80211_sta_bss_param {
  *     received from the station (u64, usec)
  * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment
  * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm)
+ * @NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG: avg signal strength of (data)
+ *     ACK frame (s8, dBm)
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -3021,6 +3041,7 @@ enum nl80211_sta_info {
        NL80211_STA_INFO_RX_DURATION,
        NL80211_STA_INFO_PAD,
        NL80211_STA_INFO_ACK_SIGNAL,
+       NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG,
 
        /* keep last */
        __NL80211_STA_INFO_AFTER_LAST,
@@ -3038,6 +3059,7 @@ enum nl80211_sta_info {
  * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
  *     MSDUs (u64)
  * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_TID_STATS_TXQ_STATS: TXQ stats (nested attribute)
  * @NUM_NL80211_TID_STATS: number of attributes here
  * @NL80211_TID_STATS_MAX: highest numbered attribute here
  */
@@ -3048,12 +3070,51 @@ enum nl80211_tid_stats {
        NL80211_TID_STATS_TX_MSDU_RETRIES,
        NL80211_TID_STATS_TX_MSDU_FAILED,
        NL80211_TID_STATS_PAD,
+       NL80211_TID_STATS_TXQ_STATS,
 
        /* keep last */
        NUM_NL80211_TID_STATS,
        NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1
 };
 
+/**
+ * enum nl80211_txq_stats - per TXQ statistics attributes
+ * @__NL80211_TXQ_STATS_INVALID: attribute number 0 is reserved
+ * @NUM_NL80211_TXQ_STATS: number of attributes here
+ * @NL80211_TXQ_STATS_BACKLOG_BYTES: number of bytes currently backlogged
+ * @NL80211_TXQ_STATS_BACKLOG_PACKETS: number of packets currently
+ *      backlogged
+ * @NL80211_TXQ_STATS_FLOWS: total number of new flows seen
+ * @NL80211_TXQ_STATS_DROPS: total number of packet drops
+ * @NL80211_TXQ_STATS_ECN_MARKS: total number of packet ECN marks
+ * @NL80211_TXQ_STATS_OVERLIMIT: number of drops due to queue space overflow
+ * @NL80211_TXQ_STATS_OVERMEMORY: number of drops due to memory limit overflow
+ *      (only for per-phy stats)
+ * @NL80211_TXQ_STATS_COLLISIONS: number of hash collisions
+ * @NL80211_TXQ_STATS_TX_BYTES: total number of bytes dequeued from TXQ
+ * @NL80211_TXQ_STATS_TX_PACKETS: total number of packets dequeued from TXQ
+ * @NL80211_TXQ_STATS_MAX_FLOWS: number of flow buckets for PHY
+ * @NL80211_TXQ_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_txq_stats {
+       __NL80211_TXQ_STATS_INVALID,
+       NL80211_TXQ_STATS_BACKLOG_BYTES,
+       NL80211_TXQ_STATS_BACKLOG_PACKETS,
+       NL80211_TXQ_STATS_FLOWS,
+       NL80211_TXQ_STATS_DROPS,
+       NL80211_TXQ_STATS_ECN_MARKS,
+       NL80211_TXQ_STATS_OVERLIMIT,
+       NL80211_TXQ_STATS_OVERMEMORY,
+       NL80211_TXQ_STATS_COLLISIONS,
+       NL80211_TXQ_STATS_TX_BYTES,
+       NL80211_TXQ_STATS_TX_PACKETS,
+       NL80211_TXQ_STATS_MAX_FLOWS,
+
+       /* keep last */
+       NUM_NL80211_TXQ_STATS,
+       NL80211_TXQ_STATS_MAX = NUM_NL80211_TXQ_STATS - 1
+};
+
 /**
  * enum nl80211_mpath_flags - nl80211 mesh path flags
  *
@@ -3143,6 +3204,29 @@ enum nl80211_band_attr {
 
 #define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA
 
+/**
+ * enum nl80211_wmm_rule - regulatory wmm rule
+ *
+ * @__NL80211_WMMR_INVALID: attribute number 0 is reserved
+ * @NL80211_WMMR_CW_MIN: Minimum contention window slot.
+ * @NL80211_WMMR_CW_MAX: Maximum contention window slot.
+ * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space.
+ * @NL80211_WMMR_TXOP: Maximum allowed tx operation time.
+ * @nl80211_WMMR_MAX: highest possible wmm rule.
+ * @__NL80211_WMMR_LAST: Internal use.
+ */
+enum nl80211_wmm_rule {
+       __NL80211_WMMR_INVALID,
+       NL80211_WMMR_CW_MIN,
+       NL80211_WMMR_CW_MAX,
+       NL80211_WMMR_AIFSN,
+       NL80211_WMMR_TXOP,
+
+       /* keep last */
+       __NL80211_WMMR_LAST,
+       NL80211_WMMR_MAX = __NL80211_WMMR_LAST - 1
+};
+
 /**
  * enum nl80211_frequency_attr - frequency attributes
  * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved
@@ -3192,6 +3276,9 @@ enum nl80211_band_attr {
  *     on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
  *     on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations.
+ *     This is a nested attribute that contains the wmm limitation per AC.
+ *     (see &enum nl80211_wmm_rule)
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *     currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -3220,6 +3307,7 @@ enum nl80211_frequency_attr {
        NL80211_FREQUENCY_ATTR_IR_CONCURRENT,
        NL80211_FREQUENCY_ATTR_NO_20MHZ,
        NL80211_FREQUENCY_ATTR_NO_10MHZ,
+       NL80211_FREQUENCY_ATTR_WMM,
 
        /* keep last */
        __NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -5040,6 +5128,11 @@ enum nl80211_feature_flags {
  *     "radar detected" event.
  * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and
  *     receiving control port frames over nl80211 instead of the netdevice.
+ * @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: This Driver support data ack
+ *     rssi if firmware support, this flag is to intimate about ack rssi
+ *     support to nl80211.
+ * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate
+ *      TXQs.
  *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -5072,6 +5165,8 @@ enum nl80211_ext_feature_index {
        NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN,
        NL80211_EXT_FEATURE_DFS_OFFLOAD,
        NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211,
+       NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT,
+       NL80211_EXT_FEATURE_TXQS,
 
        /* add new features before the definition below */
        NUM_NL80211_EXT_FEATURES,
index be05e66..84e4c1d 100644 (file)
@@ -129,6 +129,7 @@ enum {
 #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 */
 
index 9b15005..cabb210 100644 (file)
@@ -327,6 +327,9 @@ enum rtattr_type_t {
        RTA_PAD,
        RTA_UID,
        RTA_TTL_PROPAGATE,
+       RTA_IP_PROTO,
+       RTA_SPORT,
+       RTA_DPORT,
        __RTA_MAX
 };
 
index d02e859..750d891 100644 (file)
@@ -278,6 +278,7 @@ enum
        LINUX_MIB_TCPMTUPSUCCESS,               /* TCPMTUPSuccess */
        LINUX_MIB_TCPDELIVERED,                 /* TCPDelivered */
        LINUX_MIB_TCPDELIVEREDCE,               /* TCPDeliveredCE */
+       LINUX_MIB_TCPACKCOMPRESSED,             /* TCPAckCompressed */
        __LINUX_MIB_MAX
 };
 
diff --git a/include/uapi/linux/vmcore.h b/include/uapi/linux/vmcore.h
new file mode 100644 (file)
index 0000000..0226196
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _UAPI_VMCORE_H
+#define _UAPI_VMCORE_H
+
+#include <linux/types.h>
+
+#define VMCOREDD_NOTE_NAME "LINUX"
+#define VMCOREDD_MAX_NAME_BYTES 44
+
+struct vmcoredd_header {
+       __u32 n_namesz; /* Name size */
+       __u32 n_descsz; /* Content size */
+       __u32 n_type;   /* NT_VMCOREDD */
+       __u8 name[8];   /* LINUX\0\0\0 */
+       __u8 dump_name[VMCOREDD_MAX_NAME_BYTES]; /* Device dump's name */
+};
+
+#endif /* _UAPI_VMCORE_H */
index f013afc..1fecd5b 100644 (file)
@@ -738,7 +738,7 @@ config CFS_BANDWIDTH
          tasks running within the fair group scheduler.  Groups with no limit
          set are considered to be unconstrained and will run with no
          restriction.
-         See tip/Documentation/scheduler/sched-bwc.txt for more information.
+         See Documentation/scheduler/sched-bwc.txt for more information.
 
 config RT_GROUP_SCHED
        bool "Group scheduling for SCHED_RR/FIFO"
@@ -1391,6 +1391,7 @@ config BPF_SYSCALL
        bool "Enable bpf() system call"
        select ANON_INODES
        select BPF
+       select IRQ_WORK
        default n
        help
          Enable the bpf() system call that allows to manipulate eBPF
index a404936..fd37315 100644 (file)
@@ -1034,6 +1034,13 @@ __setup("rodata=", set_debug_rodata);
 static void mark_readonly(void)
 {
        if (rodata_enabled) {
+               /*
+                * load_module() results in W+X mappings, which are cleaned up
+                * with call_rcu_sched().  Let's make sure that queued work is
+                * flushed so that we don't hit false positives looking for
+                * insecure pages which are W+X.
+                */
+               rcu_barrier_sched();
                mark_rodata_ro();
                rodata_test();
        } else
index 22e1046..ded10ab 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/file.h>
 #include <linux/uaccess.h>
 #include <linux/kernel.h>
+#include <linux/idr.h>
 #include <linux/bpf_verifier.h>
 #include <linux/btf.h>
 
             i < btf_type_vlen(struct_type);                            \
             i++, member++)
 
+static DEFINE_IDR(btf_idr);
+static DEFINE_SPINLOCK(btf_idr_lock);
+
 struct btf {
        union {
                struct btf_header *hdr;
@@ -193,6 +197,8 @@ struct btf {
        u32 types_size;
        u32 data_size;
        refcount_t refcnt;
+       u32 id;
+       struct rcu_head rcu;
 };
 
 enum verifier_phase {
@@ -598,6 +604,42 @@ static int btf_add_type(struct btf_verifier_env *env, struct btf_type *t)
        return 0;
 }
 
+static int btf_alloc_id(struct btf *btf)
+{
+       int id;
+
+       idr_preload(GFP_KERNEL);
+       spin_lock_bh(&btf_idr_lock);
+       id = idr_alloc_cyclic(&btf_idr, btf, 1, INT_MAX, GFP_ATOMIC);
+       if (id > 0)
+               btf->id = id;
+       spin_unlock_bh(&btf_idr_lock);
+       idr_preload_end();
+
+       if (WARN_ON_ONCE(!id))
+               return -ENOSPC;
+
+       return id > 0 ? 0 : id;
+}
+
+static void btf_free_id(struct btf *btf)
+{
+       unsigned long flags;
+
+       /*
+        * In map-in-map, calling map_delete_elem() on outer
+        * map will call bpf_map_put on the inner map.
+        * It will then eventually call btf_free_id()
+        * on the inner map.  Some of the map_delete_elem()
+        * implementation may have irq disabled, so
+        * we need to use the _irqsave() version instead
+        * of the _bh() version.
+        */
+       spin_lock_irqsave(&btf_idr_lock, flags);
+       idr_remove(&btf_idr, btf->id);
+       spin_unlock_irqrestore(&btf_idr_lock, flags);
+}
+
 static void btf_free(struct btf *btf)
 {
        kvfree(btf->types);
@@ -607,15 +649,19 @@ static void btf_free(struct btf *btf)
        kfree(btf);
 }
 
-static void btf_get(struct btf *btf)
+static void btf_free_rcu(struct rcu_head *rcu)
 {
-       refcount_inc(&btf->refcnt);
+       struct btf *btf = container_of(rcu, struct btf, rcu);
+
+       btf_free(btf);
 }
 
 void btf_put(struct btf *btf)
 {
-       if (btf && refcount_dec_and_test(&btf->refcnt))
-               btf_free(btf);
+       if (btf && refcount_dec_and_test(&btf->refcnt)) {
+               btf_free_id(btf);
+               call_rcu(&btf->rcu, btf_free_rcu);
+       }
 }
 
 static int env_resolve_init(struct btf_verifier_env *env)
@@ -1977,7 +2023,7 @@ static struct btf *btf_parse(void __user *btf_data, u32 btf_data_size,
 
        if (!err) {
                btf_verifier_env_free(env);
-               btf_get(btf);
+               refcount_set(&btf->refcnt, 1);
                return btf;
        }
 
@@ -2006,10 +2052,15 @@ const struct file_operations btf_fops = {
        .release        = btf_release,
 };
 
+static int __btf_new_fd(struct btf *btf)
+{
+       return anon_inode_getfd("btf", &btf_fops, btf, O_RDONLY | O_CLOEXEC);
+}
+
 int btf_new_fd(const union bpf_attr *attr)
 {
        struct btf *btf;
-       int fd;
+       int ret;
 
        btf = btf_parse(u64_to_user_ptr(attr->btf),
                        attr->btf_size, attr->btf_log_level,
@@ -2018,12 +2069,23 @@ int btf_new_fd(const union bpf_attr *attr)
        if (IS_ERR(btf))
                return PTR_ERR(btf);
 
-       fd = anon_inode_getfd("btf", &btf_fops, btf,
-                             O_RDONLY | O_CLOEXEC);
-       if (fd < 0)
+       ret = btf_alloc_id(btf);
+       if (ret) {
+               btf_free(btf);
+               return ret;
+       }
+
+       /*
+        * The BTF ID is published to the userspace.
+        * All BTF free must go through call_rcu() from
+        * now on (i.e. free by calling btf_put()).
+        */
+
+       ret = __btf_new_fd(btf);
+       if (ret < 0)
                btf_put(btf);
 
-       return fd;
+       return ret;
 }
 
 struct btf *btf_get_by_fd(int fd)
@@ -2042,7 +2104,7 @@ struct btf *btf_get_by_fd(int fd)
        }
 
        btf = f.file->private_data;
-       btf_get(btf);
+       refcount_inc(&btf->refcnt);
        fdput(f);
 
        return btf;
@@ -2052,13 +2114,55 @@ int btf_get_info_by_fd(const struct btf *btf,
                       const union bpf_attr *attr,
                       union bpf_attr __user *uattr)
 {
-       void __user *udata = u64_to_user_ptr(attr->info.info);
-       u32 copy_len = min_t(u32, btf->data_size,
-                            attr->info.info_len);
+       struct bpf_btf_info __user *uinfo;
+       struct bpf_btf_info info = {};
+       u32 info_copy, btf_copy;
+       void __user *ubtf;
+       u32 uinfo_len;
+
+       uinfo = u64_to_user_ptr(attr->info.info);
+       uinfo_len = attr->info.info_len;
 
-       if (copy_to_user(udata, btf->data, copy_len) ||
-           put_user(btf->data_size, &uattr->info.info_len))
+       info_copy = min_t(u32, uinfo_len, sizeof(info));
+       if (copy_from_user(&info, uinfo, info_copy))
+               return -EFAULT;
+
+       info.id = btf->id;
+       ubtf = u64_to_user_ptr(info.btf);
+       btf_copy = min_t(u32, btf->data_size, info.btf_size);
+       if (copy_to_user(ubtf, btf->data, btf_copy))
+               return -EFAULT;
+       info.btf_size = btf->data_size;
+
+       if (copy_to_user(uinfo, &info, info_copy) ||
+           put_user(info_copy, &uattr->info.info_len))
                return -EFAULT;
 
        return 0;
 }
+
+int btf_get_fd_by_id(u32 id)
+{
+       struct btf *btf;
+       int fd;
+
+       rcu_read_lock();
+       btf = idr_find(&btf_idr, id);
+       if (!btf || !refcount_inc_not_zero(&btf->refcnt))
+               btf = ERR_PTR(-ENOENT);
+       rcu_read_unlock();
+
+       if (IS_ERR(btf))
+               return PTR_ERR(btf);
+
+       fd = __btf_new_fd(btf);
+       if (fd < 0)
+               btf_put(btf);
+
+       return fd;
+}
+
+u32 btf_id(const struct btf *btf)
+{
+       return btf->id;
+}
index d0d7d94..b574ddd 100644 (file)
@@ -219,47 +219,84 @@ int bpf_prog_calc_tag(struct bpf_prog *fp)
        return 0;
 }
 
-static void bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta)
+static int bpf_adj_delta_to_imm(struct bpf_insn *insn, u32 pos, u32 delta,
+                               u32 curr, const bool probe_pass)
 {
+       const s64 imm_min = S32_MIN, imm_max = S32_MAX;
+       s64 imm = insn->imm;
+
+       if (curr < pos && curr + imm + 1 > pos)
+               imm += delta;
+       else if (curr > pos + delta && curr + imm + 1 <= pos + delta)
+               imm -= delta;
+       if (imm < imm_min || imm > imm_max)
+               return -ERANGE;
+       if (!probe_pass)
+               insn->imm = imm;
+       return 0;
+}
+
+static int bpf_adj_delta_to_off(struct bpf_insn *insn, u32 pos, u32 delta,
+                               u32 curr, const bool probe_pass)
+{
+       const s32 off_min = S16_MIN, off_max = S16_MAX;
+       s32 off = insn->off;
+
+       if (curr < pos && curr + off + 1 > pos)
+               off += delta;
+       else if (curr > pos + delta && curr + off + 1 <= pos + delta)
+               off -= delta;
+       if (off < off_min || off > off_max)
+               return -ERANGE;
+       if (!probe_pass)
+               insn->off = off;
+       return 0;
+}
+
+static int bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta,
+                           const bool probe_pass)
+{
+       u32 i, insn_cnt = prog->len + (probe_pass ? delta : 0);
        struct bpf_insn *insn = prog->insnsi;
-       u32 i, insn_cnt = prog->len;
-       bool pseudo_call;
-       u8 code;
-       int off;
+       int ret = 0;
 
        for (i = 0; i < insn_cnt; i++, insn++) {
+               u8 code;
+
+               /* In the probing pass we still operate on the original,
+                * unpatched image in order to check overflows before we
+                * do any other adjustments. Therefore skip the patchlet.
+                */
+               if (probe_pass && i == pos) {
+                       i += delta + 1;
+                       insn++;
+               }
                code = insn->code;
-               if (BPF_CLASS(code) != BPF_JMP)
-                       continue;
-               if (BPF_OP(code) == BPF_EXIT)
+               if (BPF_CLASS(code) != BPF_JMP ||
+                   BPF_OP(code) == BPF_EXIT)
                        continue;
+               /* Adjust offset of jmps if we cross patch boundaries. */
                if (BPF_OP(code) == BPF_CALL) {
-                       if (insn->src_reg == BPF_PSEUDO_CALL)
-                               pseudo_call = true;
-                       else
+                       if (insn->src_reg != BPF_PSEUDO_CALL)
                                continue;
+                       ret = bpf_adj_delta_to_imm(insn, pos, delta, i,
+                                                  probe_pass);
                } else {
-                       pseudo_call = false;
+                       ret = bpf_adj_delta_to_off(insn, pos, delta, i,
+                                                  probe_pass);
                }
-               off = pseudo_call ? insn->imm : insn->off;
-
-               /* Adjust offset of jmps if we cross boundaries. */
-               if (i < pos && i + off + 1 > pos)
-                       off += delta;
-               else if (i > pos + delta && i + off + 1 <= pos + delta)
-                       off -= delta;
-
-               if (pseudo_call)
-                       insn->imm = off;
-               else
-                       insn->off = off;
+               if (ret)
+                       break;
        }
+
+       return ret;
 }
 
 struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
                                       const struct bpf_insn *patch, u32 len)
 {
        u32 insn_adj_cnt, insn_rest, insn_delta = len - 1;
+       const u32 cnt_max = S16_MAX;
        struct bpf_prog *prog_adj;
 
        /* Since our patchlet doesn't expand the image, we're done. */
@@ -270,6 +307,15 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
 
        insn_adj_cnt = prog->len + insn_delta;
 
+       /* Reject anything that would potentially let the insn->off
+        * target overflow when we have excessive program expansions.
+        * We need to probe here before we do any reallocation where
+        * we afterwards may not fail anymore.
+        */
+       if (insn_adj_cnt > cnt_max &&
+           bpf_adj_branches(prog, off, insn_delta, true))
+               return NULL;
+
        /* Several new instructions need to be inserted. Make room
         * for them. Likely, there's no need for a new allocation as
         * last page could have large enough tailroom.
@@ -295,7 +341,11 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
                sizeof(*patch) * insn_rest);
        memcpy(prog_adj->insnsi + off, patch, sizeof(*patch) * len);
 
-       bpf_adj_branches(prog_adj, off, insn_delta);
+       /* We are guaranteed to not fail at this point, otherwise
+        * the ship has sailed to reverse to the original state. An
+        * overflow cannot happen at this point.
+        */
+       BUG_ON(bpf_adj_branches(prog_adj, off, insn_delta, false));
 
        return prog_adj;
 }
@@ -1707,6 +1757,7 @@ const struct bpf_func_proto bpf_get_current_pid_tgid_proto __weak;
 const struct bpf_func_proto bpf_get_current_uid_gid_proto __weak;
 const struct bpf_func_proto bpf_get_current_comm_proto __weak;
 const struct bpf_func_proto bpf_sock_map_update_proto __weak;
+const struct bpf_func_proto bpf_sock_hash_update_proto __weak;
 
 const struct bpf_func_proto * __weak bpf_get_trace_printk_proto(void)
 {
index 098eca5..cd83225 100644 (file)
 #define SOCK_CREATE_FLAG_MASK \
        (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
 
-struct bpf_stab {
-       struct bpf_map map;
-       struct sock **sock_map;
+struct bpf_sock_progs {
        struct bpf_prog *bpf_tx_msg;
        struct bpf_prog *bpf_parse;
        struct bpf_prog *bpf_verdict;
 };
 
+struct bpf_stab {
+       struct bpf_map map;
+       struct sock **sock_map;
+       struct bpf_sock_progs progs;
+};
+
+struct bucket {
+       struct hlist_head head;
+       raw_spinlock_t lock;
+};
+
+struct bpf_htab {
+       struct bpf_map map;
+       struct bucket *buckets;
+       atomic_t count;
+       u32 n_buckets;
+       u32 elem_size;
+       struct bpf_sock_progs progs;
+};
+
+struct htab_elem {
+       struct rcu_head rcu;
+       struct hlist_node hash_node;
+       u32 hash;
+       struct sock *sk;
+       char key[0];
+};
+
 enum smap_psock_state {
        SMAP_TX_RUNNING,
 };
@@ -63,6 +89,8 @@ enum smap_psock_state {
 struct smap_psock_map_entry {
        struct list_head list;
        struct sock **entry;
+       struct htab_elem *hash_link;
+       struct bpf_htab *htab;
 };
 
 struct smap_psock {
@@ -191,6 +219,12 @@ out:
        rcu_read_unlock();
 }
 
+static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l)
+{
+       atomic_dec(&htab->count);
+       kfree_rcu(l, rcu);
+}
+
 static void bpf_tcp_close(struct sock *sk, long timeout)
 {
        void (*close_fun)(struct sock *sk, long timeout);
@@ -227,10 +261,16 @@ static void bpf_tcp_close(struct sock *sk, long timeout)
        }
 
        list_for_each_entry_safe(e, tmp, &psock->maps, list) {
-               osk = cmpxchg(e->entry, sk, NULL);
-               if (osk == sk) {
-                       list_del(&e->list);
-                       smap_release_sock(psock, sk);
+               if (e->entry) {
+                       osk = cmpxchg(e->entry, sk, NULL);
+                       if (osk == sk) {
+                               list_del(&e->list);
+                               smap_release_sock(psock, sk);
+                       }
+               } else {
+                       hlist_del_rcu(&e->hash_link->hash_node);
+                       smap_release_sock(psock, e->hash_link->sk);
+                       free_htab_elem(e->htab, e->hash_link);
                }
        }
        write_unlock_bh(&sk->sk_callback_lock);
@@ -461,7 +501,7 @@ static int free_curr_sg(struct sock *sk, struct sk_msg_buff *md)
 static int bpf_map_msg_verdict(int _rc, struct sk_msg_buff *md)
 {
        return ((_rc == SK_PASS) ?
-              (md->map ? __SK_REDIRECT : __SK_PASS) :
+              (md->sk_redir ? __SK_REDIRECT : __SK_PASS) :
               __SK_DROP);
 }
 
@@ -1092,7 +1132,7 @@ static int smap_verdict_func(struct smap_psock *psock, struct sk_buff *skb)
         * when we orphan the skb so that we don't have the possibility
         * to reference a stale map.
         */
-       TCP_SKB_CB(skb)->bpf.map = NULL;
+       TCP_SKB_CB(skb)->bpf.sk_redir = NULL;
        skb->sk = psock->sock;
        bpf_compute_data_pointers(skb);
        preempt_disable();
@@ -1102,7 +1142,7 @@ static int smap_verdict_func(struct smap_psock *psock, struct sk_buff *skb)
 
        /* Moving return codes from UAPI namespace into internal namespace */
        return rc == SK_PASS ?
-               (TCP_SKB_CB(skb)->bpf.map ? __SK_REDIRECT : __SK_PASS) :
+               (TCP_SKB_CB(skb)->bpf.sk_redir ? __SK_REDIRECT : __SK_PASS) :
                __SK_DROP;
 }
 
@@ -1372,7 +1412,6 @@ static int smap_init_sock(struct smap_psock *psock,
 }
 
 static void smap_init_progs(struct smap_psock *psock,
-                           struct bpf_stab *stab,
                            struct bpf_prog *verdict,
                            struct bpf_prog *parse)
 {
@@ -1450,14 +1489,13 @@ static void smap_gc_work(struct work_struct *w)
        kfree(psock);
 }
 
-static struct smap_psock *smap_init_psock(struct sock *sock,
-                                         struct bpf_stab *stab)
+static struct smap_psock *smap_init_psock(struct sock *sock, int node)
 {
        struct smap_psock *psock;
 
        psock = kzalloc_node(sizeof(struct smap_psock),
                             GFP_ATOMIC | __GFP_NOWARN,
-                            stab->map.numa_node);
+                            node);
        if (!psock)
                return ERR_PTR(-ENOMEM);
 
@@ -1525,12 +1563,14 @@ free_stab:
        return ERR_PTR(err);
 }
 
-static void smap_list_remove(struct smap_psock *psock, struct sock **entry)
+static void smap_list_remove(struct smap_psock *psock,
+                            struct sock **entry,
+                            struct htab_elem *hash_link)
 {
        struct smap_psock_map_entry *e, *tmp;
 
        list_for_each_entry_safe(e, tmp, &psock->maps, list) {
-               if (e->entry == entry) {
+               if (e->entry == entry || e->hash_link == hash_link) {
                        list_del(&e->list);
                        break;
                }
@@ -1568,7 +1608,7 @@ static void sock_map_free(struct bpf_map *map)
                 * to be null and queued for garbage collection.
                 */
                if (likely(psock)) {
-                       smap_list_remove(psock, &stab->sock_map[i]);
+                       smap_list_remove(psock, &stab->sock_map[i], NULL);
                        smap_release_sock(psock, sock);
                }
                write_unlock_bh(&sock->sk_callback_lock);
@@ -1627,7 +1667,7 @@ static int sock_map_delete_elem(struct bpf_map *map, void *key)
 
        if (psock->bpf_parse)
                smap_stop_sock(psock, sock);
-       smap_list_remove(psock, &stab->sock_map[k]);
+       smap_list_remove(psock, &stab->sock_map[k], NULL);
        smap_release_sock(psock, sock);
 out:
        write_unlock_bh(&sock->sk_callback_lock);
@@ -1662,40 +1702,26 @@ out:
  *  - sock_map must use READ_ONCE and (cmp)xchg operations
  *  - BPF verdict/parse programs must use READ_ONCE and xchg operations
  */
-static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
-                                   struct bpf_map *map,
-                                   void *key, u64 flags)
+
+static int __sock_map_ctx_update_elem(struct bpf_map *map,
+                                     struct bpf_sock_progs *progs,
+                                     struct sock *sock,
+                                     struct sock **map_link,
+                                     void *key)
 {
-       struct bpf_stab *stab = container_of(map, struct bpf_stab, map);
-       struct smap_psock_map_entry *e = NULL;
        struct bpf_prog *verdict, *parse, *tx_msg;
-       struct sock *osock, *sock;
+       struct smap_psock_map_entry *e = NULL;
        struct smap_psock *psock;
-       u32 i = *(u32 *)key;
        bool new = false;
        int err;
 
-       if (unlikely(flags > BPF_EXIST))
-               return -EINVAL;
-
-       if (unlikely(i >= stab->map.max_entries))
-               return -E2BIG;
-
-       sock = READ_ONCE(stab->sock_map[i]);
-       if (flags == BPF_EXIST && !sock)
-               return -ENOENT;
-       else if (flags == BPF_NOEXIST && sock)
-               return -EEXIST;
-
-       sock = skops->sk;
-
        /* 1. If sock map has BPF programs those will be inherited by the
         * sock being added. If the sock is already attached to BPF programs
         * this results in an error.
         */
-       verdict = READ_ONCE(stab->bpf_verdict);
-       parse = READ_ONCE(stab->bpf_parse);
-       tx_msg = READ_ONCE(stab->bpf_tx_msg);
+       verdict = READ_ONCE(progs->bpf_verdict);
+       parse = READ_ONCE(progs->bpf_parse);
+       tx_msg = READ_ONCE(progs->bpf_tx_msg);
 
        if (parse && verdict) {
                /* bpf prog refcnt may be zero if a concurrent attach operation
@@ -1703,11 +1729,11 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
                 * we increment the refcnt. If this is the case abort with an
                 * error.
                 */
-               verdict = bpf_prog_inc_not_zero(stab->bpf_verdict);
+               verdict = bpf_prog_inc_not_zero(verdict);
                if (IS_ERR(verdict))
                        return PTR_ERR(verdict);
 
-               parse = bpf_prog_inc_not_zero(stab->bpf_parse);
+               parse = bpf_prog_inc_not_zero(parse);
                if (IS_ERR(parse)) {
                        bpf_prog_put(verdict);
                        return PTR_ERR(parse);
@@ -1715,12 +1741,12 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
        }
 
        if (tx_msg) {
-               tx_msg = bpf_prog_inc_not_zero(stab->bpf_tx_msg);
+               tx_msg = bpf_prog_inc_not_zero(tx_msg);
                if (IS_ERR(tx_msg)) {
-                       if (verdict)
-                               bpf_prog_put(verdict);
-                       if (parse)
+                       if (parse && verdict) {
                                bpf_prog_put(parse);
+                               bpf_prog_put(verdict);
+                       }
                        return PTR_ERR(tx_msg);
                }
        }
@@ -1748,7 +1774,7 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
                        goto out_progs;
                }
        } else {
-               psock = smap_init_psock(sock, stab);
+               psock = smap_init_psock(sock, map->numa_node);
                if (IS_ERR(psock)) {
                        err = PTR_ERR(psock);
                        goto out_progs;
@@ -1758,12 +1784,13 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
                new = true;
        }
 
-       e = kzalloc(sizeof(*e), GFP_ATOMIC | __GFP_NOWARN);
-       if (!e) {
-               err = -ENOMEM;
-               goto out_progs;
+       if (map_link) {
+               e = kzalloc(sizeof(*e), GFP_ATOMIC | __GFP_NOWARN);
+               if (!e) {
+                       err = -ENOMEM;
+                       goto out_progs;
+               }
        }
-       e->entry = &stab->sock_map[i];
 
        /* 3. At this point we have a reference to a valid psock that is
         * running. Attach any BPF programs needed.
@@ -1780,7 +1807,7 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
                err = smap_init_sock(psock, sock);
                if (err)
                        goto out_free;
-               smap_init_progs(psock, stab, verdict, parse);
+               smap_init_progs(psock, verdict, parse);
                smap_start_sock(psock, sock);
        }
 
@@ -1789,50 +1816,94 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
         * it with. Because we can only have a single set of programs if
         * old_sock has a strp we can stop it.
         */
-       list_add_tail(&e->list, &psock->maps);
+       if (map_link) {
+               e->entry = map_link;
+               list_add_tail(&e->list, &psock->maps);
+       }
        write_unlock_bh(&sock->sk_callback_lock);
+       return err;
+out_free:
+       kfree(e);
+       smap_release_sock(psock, sock);
+out_progs:
+       if (parse && verdict) {
+               bpf_prog_put(parse);
+               bpf_prog_put(verdict);
+       }
+       if (tx_msg)
+               bpf_prog_put(tx_msg);
+       write_unlock_bh(&sock->sk_callback_lock);
+       kfree(e);
+       return err;
+}
+
+static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
+                                   struct bpf_map *map,
+                                   void *key, u64 flags)
+{
+       struct bpf_stab *stab = container_of(map, struct bpf_stab, map);
+       struct bpf_sock_progs *progs = &stab->progs;
+       struct sock *osock, *sock;
+       u32 i = *(u32 *)key;
+       int err;
+
+       if (unlikely(flags > BPF_EXIST))
+               return -EINVAL;
+
+       if (unlikely(i >= stab->map.max_entries))
+               return -E2BIG;
+
+       sock = READ_ONCE(stab->sock_map[i]);
+       if (flags == BPF_EXIST && !sock)
+               return -ENOENT;
+       else if (flags == BPF_NOEXIST && sock)
+               return -EEXIST;
+
+       sock = skops->sk;
+       err = __sock_map_ctx_update_elem(map, progs, sock, &stab->sock_map[i],
+                                        key);
+       if (err)
+               goto out;
 
        osock = xchg(&stab->sock_map[i], sock);
        if (osock) {
                struct smap_psock *opsock = smap_psock_sk(osock);
 
                write_lock_bh(&osock->sk_callback_lock);
-               smap_list_remove(opsock, &stab->sock_map[i]);
+               smap_list_remove(opsock, &stab->sock_map[i], NULL);
                smap_release_sock(opsock, osock);
                write_unlock_bh(&osock->sk_callback_lock);
        }
-       return 0;
-out_free:
-       smap_release_sock(psock, sock);
-out_progs:
-       if (verdict)
-               bpf_prog_put(verdict);
-       if (parse)
-               bpf_prog_put(parse);
-       if (tx_msg)
-               bpf_prog_put(tx_msg);
-       write_unlock_bh(&sock->sk_callback_lock);
-       kfree(e);
+out:
        return err;
 }
 
 int sock_map_prog(struct bpf_map *map, struct bpf_prog *prog, u32 type)
 {
-       struct bpf_stab *stab = container_of(map, struct bpf_stab, map);
+       struct bpf_sock_progs *progs;
        struct bpf_prog *orig;
 
-       if (unlikely(map->map_type != BPF_MAP_TYPE_SOCKMAP))
+       if (map->map_type == BPF_MAP_TYPE_SOCKMAP) {
+               struct bpf_stab *stab = container_of(map, struct bpf_stab, map);
+
+               progs = &stab->progs;
+       } else if (map->map_type == BPF_MAP_TYPE_SOCKHASH) {
+               struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
+
+               progs = &htab->progs;
+       } else {
                return -EINVAL;
+       }
 
        switch (type) {
        case BPF_SK_MSG_VERDICT:
-               orig = xchg(&stab->bpf_tx_msg, prog);
+               orig = xchg(&progs->bpf_tx_msg, prog);
                break;
        case BPF_SK_SKB_STREAM_PARSER:
-               orig = xchg(&stab->bpf_parse, prog);
+               orig = xchg(&progs->bpf_parse, prog);
                break;
        case BPF_SK_SKB_STREAM_VERDICT:
-               orig = xchg(&stab->bpf_verdict, prog);
+               orig = xchg(&progs->bpf_verdict, prog);
                break;
        default:
                return -EOPNOTSUPP;
@@ -1880,21 +1951,421 @@ static int sock_map_update_elem(struct bpf_map *map,
 
 static void sock_map_release(struct bpf_map *map)
 {
-       struct bpf_stab *stab = container_of(map, struct bpf_stab, map);
+       struct bpf_sock_progs *progs;
        struct bpf_prog *orig;
 
-       orig = xchg(&stab->bpf_parse, NULL);
+       if (map->map_type == BPF_MAP_TYPE_SOCKMAP) {
+               struct bpf_stab *stab = container_of(map, struct bpf_stab, map);
+
+               progs = &stab->progs;
+       } else {
+               struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
+
+               progs = &htab->progs;
+       }
+
+       orig = xchg(&progs->bpf_parse, NULL);
        if (orig)
                bpf_prog_put(orig);
-       orig = xchg(&stab->bpf_verdict, NULL);
+       orig = xchg(&progs->bpf_verdict, NULL);
        if (orig)
                bpf_prog_put(orig);
 
-       orig = xchg(&stab->bpf_tx_msg, NULL);
+       orig = xchg(&progs->bpf_tx_msg, NULL);
        if (orig)
                bpf_prog_put(orig);
 }
 
+static struct bpf_map *sock_hash_alloc(union bpf_attr *attr)
+{
+       struct bpf_htab *htab;
+       int i, err;
+       u64 cost;
+
+       if (!capable(CAP_NET_ADMIN))
+               return ERR_PTR(-EPERM);
+
+       /* check sanity of attributes */
+       if (attr->max_entries == 0 || attr->value_size != 4 ||
+           attr->map_flags & ~SOCK_CREATE_FLAG_MASK)
+               return ERR_PTR(-EINVAL);
+
+       if (attr->key_size > MAX_BPF_STACK)
+               /* eBPF programs initialize keys on stack, so they cannot be
+                * larger than max stack size
+                */
+               return ERR_PTR(-E2BIG);
+
+       err = bpf_tcp_ulp_register();
+       if (err && err != -EEXIST)
+               return ERR_PTR(err);
+
+       htab = kzalloc(sizeof(*htab), GFP_USER);
+       if (!htab)
+               return ERR_PTR(-ENOMEM);
+
+       bpf_map_init_from_attr(&htab->map, attr);
+
+       htab->n_buckets = roundup_pow_of_two(htab->map.max_entries);
+       htab->elem_size = sizeof(struct htab_elem) +
+                         round_up(htab->map.key_size, 8);
+       err = -EINVAL;
+       if (htab->n_buckets == 0 ||
+           htab->n_buckets > U32_MAX / sizeof(struct bucket))
+               goto free_htab;
+
+       cost = (u64) htab->n_buckets * sizeof(struct bucket) +
+              (u64) htab->elem_size * htab->map.max_entries;
+
+       if (cost >= U32_MAX - PAGE_SIZE)
+               goto free_htab;
+
+       htab->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT;
+       err = bpf_map_precharge_memlock(htab->map.pages);
+       if (err)
+               goto free_htab;
+
+       err = -ENOMEM;
+       htab->buckets = bpf_map_area_alloc(
+                               htab->n_buckets * sizeof(struct bucket),
+                               htab->map.numa_node);
+       if (!htab->buckets)
+               goto free_htab;
+
+       for (i = 0; i < htab->n_buckets; i++) {
+               INIT_HLIST_HEAD(&htab->buckets[i].head);
+               raw_spin_lock_init(&htab->buckets[i].lock);
+       }
+
+       return &htab->map;
+free_htab:
+       kfree(htab);
+       return ERR_PTR(err);
+}
+
+static inline struct bucket *__select_bucket(struct bpf_htab *htab, u32 hash)
+{
+       return &htab->buckets[hash & (htab->n_buckets - 1)];
+}
+
+static inline struct hlist_head *select_bucket(struct bpf_htab *htab, u32 hash)
+{
+       return &__select_bucket(htab, hash)->head;
+}
+
+static void sock_hash_free(struct bpf_map *map)
+{
+       struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
+       int i;
+
+       synchronize_rcu();
+
+       /* At this point no update, lookup or delete operations can happen.
+        * However, be aware we can still get a socket state event updates,
+        * and data ready callabacks that reference the psock from sk_user_data
+        * Also psock worker threads are still in-flight. So smap_release_sock
+        * will only free the psock after cancel_sync on the worker threads
+        * and a grace period expire to ensure psock is really safe to remove.
+        */
+       rcu_read_lock();
+       for (i = 0; i < htab->n_buckets; i++) {
+               struct hlist_head *head = select_bucket(htab, i);
+               struct hlist_node *n;
+               struct htab_elem *l;
+
+               hlist_for_each_entry_safe(l, n, head, hash_node) {
+                       struct sock *sock = l->sk;
+                       struct smap_psock *psock;
+
+                       hlist_del_rcu(&l->hash_node);
+                       write_lock_bh(&sock->sk_callback_lock);
+                       psock = smap_psock_sk(sock);
+                       /* This check handles a racing sock event that can get
+                        * the sk_callback_lock before this case but after xchg
+                        * causing the refcnt to hit zero and sock user data
+                        * (psock) to be null and queued for garbage collection.
+                        */
+                       if (likely(psock)) {
+                               smap_list_remove(psock, NULL, l);
+                               smap_release_sock(psock, sock);
+                       }
+                       write_unlock_bh(&sock->sk_callback_lock);
+                       kfree(l);
+               }
+       }
+       rcu_read_unlock();
+       bpf_map_area_free(htab->buckets);
+       kfree(htab);
+}
+
+static struct htab_elem *alloc_sock_hash_elem(struct bpf_htab *htab,
+                                             void *key, u32 key_size, u32 hash,
+                                             struct sock *sk,
+                                             struct htab_elem *old_elem)
+{
+       struct htab_elem *l_new;
+
+       if (atomic_inc_return(&htab->count) > htab->map.max_entries) {
+               if (!old_elem) {
+                       atomic_dec(&htab->count);
+                       return ERR_PTR(-E2BIG);
+               }
+       }
+       l_new = kmalloc_node(htab->elem_size, GFP_ATOMIC | __GFP_NOWARN,
+                            htab->map.numa_node);
+       if (!l_new)
+               return ERR_PTR(-ENOMEM);
+
+       memcpy(l_new->key, key, key_size);
+       l_new->sk = sk;
+       l_new->hash = hash;
+       return l_new;
+}
+
+static struct htab_elem *lookup_elem_raw(struct hlist_head *head,
+                                        u32 hash, void *key, u32 key_size)
+{
+       struct htab_elem *l;
+
+       hlist_for_each_entry_rcu(l, head, hash_node) {
+               if (l->hash == hash && !memcmp(&l->key, key, key_size))
+                       return l;
+       }
+
+       return NULL;
+}
+
+static inline u32 htab_map_hash(const void *key, u32 key_len)
+{
+       return jhash(key, key_len, 0);
+}
+
+static int sock_hash_get_next_key(struct bpf_map *map,
+                                 void *key, void *next_key)
+{
+       struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
+       struct htab_elem *l, *next_l;
+       struct hlist_head *h;
+       u32 hash, key_size;
+       int i = 0;
+
+       WARN_ON_ONCE(!rcu_read_lock_held());
+
+       key_size = map->key_size;
+       if (!key)
+               goto find_first_elem;
+       hash = htab_map_hash(key, key_size);
+       h = select_bucket(htab, hash);
+
+       l = lookup_elem_raw(h, hash, key, key_size);
+       if (!l)
+               goto find_first_elem;
+       next_l = hlist_entry_safe(
+                    rcu_dereference_raw(hlist_next_rcu(&l->hash_node)),
+                    struct htab_elem, hash_node);
+       if (next_l) {
+               memcpy(next_key, next_l->key, key_size);
+               return 0;
+       }
+
+       /* no more elements in this hash list, go to the next bucket */
+       i = hash & (htab->n_buckets - 1);
+       i++;
+
+find_first_elem:
+       /* iterate over buckets */
+       for (; i < htab->n_buckets; i++) {
+               h = select_bucket(htab, i);
+
+               /* pick first element in the bucket */
+               next_l = hlist_entry_safe(
+                               rcu_dereference_raw(hlist_first_rcu(h)),
+                               struct htab_elem, hash_node);
+               if (next_l) {
+                       /* if it's not empty, just return it */
+                       memcpy(next_key, next_l->key, key_size);
+                       return 0;
+               }
+       }
+
+       /* iterated over all buckets and all elements */
+       return -ENOENT;
+}
+
+static int sock_hash_ctx_update_elem(struct bpf_sock_ops_kern *skops,
+                                    struct bpf_map *map,
+                                    void *key, u64 map_flags)
+{
+       struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
+       struct bpf_sock_progs *progs = &htab->progs;
+       struct htab_elem *l_new = NULL, *l_old;
+       struct smap_psock_map_entry *e = NULL;
+       struct hlist_head *head;
+       struct smap_psock *psock;
+       u32 key_size, hash;
+       struct sock *sock;
+       struct bucket *b;
+       int err;
+
+       sock = skops->sk;
+
+       if (sock->sk_type != SOCK_STREAM ||
+           sock->sk_protocol != IPPROTO_TCP)
+               return -EOPNOTSUPP;
+
+       if (unlikely(map_flags > BPF_EXIST))
+               return -EINVAL;
+
+       e = kzalloc(sizeof(*e), GFP_ATOMIC | __GFP_NOWARN);
+       if (!e)
+               return -ENOMEM;
+
+       WARN_ON_ONCE(!rcu_read_lock_held());
+       key_size = map->key_size;
+       hash = htab_map_hash(key, key_size);
+       b = __select_bucket(htab, hash);
+       head = &b->head;
+
+       err = __sock_map_ctx_update_elem(map, progs, sock, NULL, key);
+       if (err)
+               goto err;
+
+       /* bpf_map_update_elem() can be called in_irq() */
+       raw_spin_lock_bh(&b->lock);
+       l_old = lookup_elem_raw(head, hash, key, key_size);
+       if (l_old && map_flags == BPF_NOEXIST) {
+               err = -EEXIST;
+               goto bucket_err;
+       }
+       if (!l_old && map_flags == BPF_EXIST) {
+               err = -ENOENT;
+               goto bucket_err;
+       }
+
+       l_new = alloc_sock_hash_elem(htab, key, key_size, hash, sock, l_old);
+       if (IS_ERR(l_new)) {
+               err = PTR_ERR(l_new);
+               goto bucket_err;
+       }
+
+       psock = smap_psock_sk(sock);
+       if (unlikely(!psock)) {
+               err = -EINVAL;
+               goto bucket_err;
+       }
+
+       e->hash_link = l_new;
+       e->htab = container_of(map, struct bpf_htab, map);
+       list_add_tail(&e->list, &psock->maps);
+
+       /* add new element to the head of the list, so that
+        * concurrent search will find it before old elem
+        */
+       hlist_add_head_rcu(&l_new->hash_node, head);
+       if (l_old) {
+               psock = smap_psock_sk(l_old->sk);
+
+               hlist_del_rcu(&l_old->hash_node);
+               smap_list_remove(psock, NULL, l_old);
+               smap_release_sock(psock, l_old->sk);
+               free_htab_elem(htab, l_old);
+       }
+       raw_spin_unlock_bh(&b->lock);
+       return 0;
+bucket_err:
+       raw_spin_unlock_bh(&b->lock);
+err:
+       kfree(e);
+       psock = smap_psock_sk(sock);
+       if (psock)
+               smap_release_sock(psock, sock);
+       return err;
+}
+
+static int sock_hash_update_elem(struct bpf_map *map,
+                               void *key, void *value, u64 flags)
+{
+       struct bpf_sock_ops_kern skops;
+       u32 fd = *(u32 *)value;
+       struct socket *socket;
+       int err;
+
+       socket = sockfd_lookup(fd, &err);
+       if (!socket)
+               return err;
+
+       skops.sk = socket->sk;
+       if (!skops.sk) {
+               fput(socket->file);
+               return -EINVAL;
+       }
+
+       err = sock_hash_ctx_update_elem(&skops, map, key, flags);
+       fput(socket->file);
+       return err;
+}
+
+static int sock_hash_delete_elem(struct bpf_map *map, void *key)
+{
+       struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
+       struct hlist_head *head;
+       struct bucket *b;
+       struct htab_elem *l;
+       u32 hash, key_size;
+       int ret = -ENOENT;
+
+       key_size = map->key_size;
+       hash = htab_map_hash(key, key_size);
+       b = __select_bucket(htab, hash);
+       head = &b->head;
+
+       raw_spin_lock_bh(&b->lock);
+       l = lookup_elem_raw(head, hash, key, key_size);
+       if (l) {
+               struct sock *sock = l->sk;
+               struct smap_psock *psock;
+
+               hlist_del_rcu(&l->hash_node);
+               write_lock_bh(&sock->sk_callback_lock);
+               psock = smap_psock_sk(sock);
+               /* This check handles a racing sock event that can get the
+                * sk_callback_lock before this case but after xchg happens
+                * causing the refcnt to hit zero and sock user data (psock)
+                * to be null and queued for garbage collection.
+                */
+               if (likely(psock)) {
+                       smap_list_remove(psock, NULL, l);
+                       smap_release_sock(psock, sock);
+               }
+               write_unlock_bh(&sock->sk_callback_lock);
+               free_htab_elem(htab, l);
+               ret = 0;
+       }
+       raw_spin_unlock_bh(&b->lock);
+       return ret;
+}
+
+struct sock  *__sock_hash_lookup_elem(struct bpf_map *map, void *key)
+{
+       struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
+       struct hlist_head *head;
+       struct htab_elem *l;
+       u32 key_size, hash;
+       struct bucket *b;
+       struct sock *sk;
+
+       key_size = map->key_size;
+       hash = htab_map_hash(key, key_size);
+       b = __select_bucket(htab, hash);
+       head = &b->head;
+
+       raw_spin_lock_bh(&b->lock);
+       l = lookup_elem_raw(head, hash, key, key_size);
+       sk = l ? l->sk : NULL;
+       raw_spin_unlock_bh(&b->lock);
+       return sk;
+}
+
 const struct bpf_map_ops sock_map_ops = {
        .map_alloc = sock_map_alloc,
        .map_free = sock_map_free,
@@ -1905,6 +2376,15 @@ const struct bpf_map_ops sock_map_ops = {
        .map_release_uref = sock_map_release,
 };
 
+const struct bpf_map_ops sock_hash_ops = {
+       .map_alloc = sock_hash_alloc,
+       .map_free = sock_hash_free,
+       .map_lookup_elem = sock_map_lookup,
+       .map_get_next_key = sock_hash_get_next_key,
+       .map_update_elem = sock_hash_update_elem,
+       .map_delete_elem = sock_hash_delete_elem,
+};
+
 BPF_CALL_4(bpf_sock_map_update, struct bpf_sock_ops_kern *, bpf_sock,
           struct bpf_map *, map, void *, key, u64, flags)
 {
@@ -1922,3 +2402,21 @@ const struct bpf_func_proto bpf_sock_map_update_proto = {
        .arg3_type      = ARG_PTR_TO_MAP_KEY,
        .arg4_type      = ARG_ANYTHING,
 };
+
+BPF_CALL_4(bpf_sock_hash_update, struct bpf_sock_ops_kern *, bpf_sock,
+          struct bpf_map *, map, void *, key, u64, flags)
+{
+       WARN_ON_ONCE(!rcu_read_lock_held());
+       return sock_hash_ctx_update_elem(bpf_sock, map, key, flags);
+}
+
+const struct bpf_func_proto bpf_sock_hash_update_proto = {
+       .func           = bpf_sock_hash_update,
+       .gpl_only       = false,
+       .pkt_access     = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_CONST_MAP_PTR,
+       .arg3_type      = ARG_PTR_TO_MAP_KEY,
+       .arg4_type      = ARG_ANYTHING,
+};
index 3ba102b..b59ace0 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/perf_event.h>
 #include <linux/elf.h>
 #include <linux/pagemap.h>
+#include <linux/irq_work.h>
 #include "percpu_freelist.h"
 
 #define STACK_CREATE_FLAG_MASK                                 \
@@ -32,6 +33,23 @@ struct bpf_stack_map {
        struct stack_map_bucket *buckets[];
 };
 
+/* irq_work to run up_read() for build_id lookup in nmi context */
+struct stack_map_irq_work {
+       struct irq_work irq_work;
+       struct rw_semaphore *sem;
+};
+
+static void do_up_read(struct irq_work *entry)
+{
+       struct stack_map_irq_work *work;
+
+       work = container_of(entry, struct stack_map_irq_work, irq_work);
+       up_read(work->sem);
+       work->sem = NULL;
+}
+
+static DEFINE_PER_CPU(struct stack_map_irq_work, up_read_work);
+
 static inline bool stack_map_use_build_id(struct bpf_map *map)
 {
        return (map->map_flags & BPF_F_STACK_BUILD_ID);
@@ -267,17 +285,27 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs,
 {
        int i;
        struct vm_area_struct *vma;
+       bool in_nmi_ctx = in_nmi();
+       bool irq_work_busy = false;
+       struct stack_map_irq_work *work;
+
+       if (in_nmi_ctx) {
+               work = this_cpu_ptr(&up_read_work);
+               if (work->irq_work.flags & IRQ_WORK_BUSY)
+                       /* cannot queue more up_read, fallback */
+                       irq_work_busy = true;
+       }
 
        /*
-        * We cannot do up_read() in nmi context, so build_id lookup is
-        * only supported for non-nmi events. If at some point, it is
-        * possible to run find_vma() without taking the semaphore, we
-        * would like to allow build_id lookup in nmi context.
+        * We cannot do up_read() in nmi context. To do build_id lookup
+        * in nmi context, we need to run up_read() in irq_work. We use
+        * a percpu variable to do the irq_work. If the irq_work is
+        * already used by another lookup, we fall back to report ips.
         *
         * Same fallback is used for kernel stack (!user) on a stackmap
         * with build_id.
         */
-       if (!user || !current || !current->mm || in_nmi() ||
+       if (!user || !current || !current->mm || irq_work_busy ||
            down_read_trylock(&current->mm->mmap_sem) == 0) {
                /* cannot access current->mm, fall back to ips */
                for (i = 0; i < trace_nr; i++) {
@@ -299,7 +327,13 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs,
                        - vma->vm_start;
                id_offs[i].status = BPF_STACK_BUILD_ID_VALID;
        }
-       up_read(&current->mm->mmap_sem);
+
+       if (!in_nmi_ctx) {
+               up_read(&current->mm->mmap_sem);
+       } else {
+               work->sem = &current->mm->mmap_sem;
+               irq_work_queue(&work->irq_work);
+       }
 }
 
 BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map,
@@ -575,3 +609,16 @@ const struct bpf_map_ops stack_map_ops = {
        .map_update_elem = stack_map_update_elem,
        .map_delete_elem = stack_map_delete_elem,
 };
+
+static int __init stack_map_init(void)
+{
+       int cpu;
+       struct stack_map_irq_work *work;
+
+       for_each_possible_cpu(cpu) {
+               work = per_cpu_ptr(&up_read_work, cpu);
+               init_irq_work(&work->irq_work, do_up_read);
+       }
+       return 0;
+}
+subsys_initcall(stack_map_init);
index c286e75..bfcde94 100644 (file)
@@ -255,7 +255,6 @@ static void bpf_map_free_deferred(struct work_struct *work)
 
        bpf_map_uncharge_memlock(map);
        security_bpf_map_free(map);
-       btf_put(map->btf);
        /* implementation dependent freeing */
        map->ops->map_free(map);
 }
@@ -276,6 +275,7 @@ static void __bpf_map_put(struct bpf_map *map, bool do_idr_lock)
        if (atomic_dec_and_test(&map->refcnt)) {
                /* bpf_map_free_id() must be called first */
                bpf_map_free_id(map, do_idr_lock);
+               btf_put(map->btf);
                INIT_WORK(&map->work, bpf_map_free_deferred);
                schedule_work(&map->work);
        }
@@ -2011,6 +2011,12 @@ static int bpf_map_get_info_by_fd(struct bpf_map *map,
        info.map_flags = map->map_flags;
        memcpy(info.name, map->name, sizeof(map->name));
 
+       if (map->btf) {
+               info.btf_id = btf_id(map->btf);
+               info.btf_key_id = map->btf_key_id;
+               info.btf_value_id = map->btf_value_id;
+       }
+
        if (bpf_map_is_dev_bound(map)) {
                err = bpf_map_offload_info_fill(&info, map);
                if (err)
@@ -2024,6 +2030,21 @@ static int bpf_map_get_info_by_fd(struct bpf_map *map,
        return 0;
 }
 
+static int bpf_btf_get_info_by_fd(struct btf *btf,
+                                 const union bpf_attr *attr,
+                                 union bpf_attr __user *uattr)
+{
+       struct bpf_btf_info __user *uinfo = u64_to_user_ptr(attr->info.info);
+       u32 info_len = attr->info.info_len;
+       int err;
+
+       err = check_uarg_tail_zero(uinfo, sizeof(*uinfo), info_len);
+       if (err)
+               return err;
+
+       return btf_get_info_by_fd(btf, attr, uattr);
+}
+
 #define BPF_OBJ_GET_INFO_BY_FD_LAST_FIELD info.info
 
 static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
@@ -2047,7 +2068,7 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
                err = bpf_map_get_info_by_fd(f.file->private_data, attr,
                                             uattr);
        else if (f.file->f_op == &btf_fops)
-               err = btf_get_info_by_fd(f.file->private_data, attr, uattr);
+               err = bpf_btf_get_info_by_fd(f.file->private_data, attr, uattr);
        else
                err = -EINVAL;
 
@@ -2068,6 +2089,19 @@ static int bpf_btf_load(const union bpf_attr *attr)
        return btf_new_fd(attr);
 }
 
+#define BPF_BTF_GET_FD_BY_ID_LAST_FIELD btf_id
+
+static int bpf_btf_get_fd_by_id(const union bpf_attr *attr)
+{
+       if (CHECK_ATTR(BPF_BTF_GET_FD_BY_ID))
+               return -EINVAL;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       return btf_get_fd_by_id(attr->btf_id);
+}
+
 SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
 {
        union bpf_attr attr = {};
@@ -2151,6 +2185,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
        case BPF_BTF_LOAD:
                err = bpf_btf_load(&attr);
                break;
+       case BPF_BTF_GET_FD_BY_ID:
+               err = bpf_btf_get_fd_by_id(&attr);
+               break;
        default:
                err = -EINVAL;
                break;
index d5e1a6c..a9e4b13 100644 (file)
@@ -2093,6 +2093,13 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
                    func_id != BPF_FUNC_msg_redirect_map)
                        goto error;
                break;
+       case BPF_MAP_TYPE_SOCKHASH:
+               if (func_id != BPF_FUNC_sk_redirect_hash &&
+                   func_id != BPF_FUNC_sock_hash_update &&
+                   func_id != BPF_FUNC_map_delete_elem &&
+                   func_id != BPF_FUNC_msg_redirect_hash)
+                       goto error;
+               break;
        default:
                break;
        }
@@ -2130,11 +2137,14 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
                break;
        case BPF_FUNC_sk_redirect_map:
        case BPF_FUNC_msg_redirect_map:
+       case BPF_FUNC_sock_map_update:
                if (map->map_type != BPF_MAP_TYPE_SOCKMAP)
                        goto error;
                break;
-       case BPF_FUNC_sock_map_update:
-               if (map->map_type != BPF_MAP_TYPE_SOCKMAP)
+       case BPF_FUNC_sk_redirect_hash:
+       case BPF_FUNC_msg_redirect_hash:
+       case BPF_FUNC_sock_hash_update:
+               if (map->map_type != BPF_MAP_TYPE_SOCKHASH)
                        goto error;
                break;
        default:
@@ -5215,7 +5225,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
                }
        }
 
-       if (!ops->convert_ctx_access)
+       if (!ops->convert_ctx_access || bpf_prog_is_dev_bound(env->prog->aux))
                return 0;
 
        insn = env->prog->insnsi + delta;
index 6c6b3c4..1d8ca9e 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/slab.h>
 #include <linux/circ_buf.h>
 #include <linux/poll.h>
+#include <linux/nospec.h>
 
 #include "internal.h"
 
@@ -867,8 +868,10 @@ perf_mmap_to_page(struct ring_buffer *rb, unsigned long pgoff)
                        return NULL;
 
                /* AUX space */
-               if (pgoff >= rb->aux_pgoff)
-                       return virt_to_page(rb->aux_pages[pgoff - rb->aux_pgoff]);
+               if (pgoff >= rb->aux_pgoff) {
+                       int aux_pgoff = array_index_nospec(pgoff - rb->aux_pgoff, rb->aux_nr_pages);
+                       return virt_to_page(rb->aux_pages[aux_pgoff]);
+               }
        }
 
        return __perf_mmap_to_page(rb, pgoff);
index cd50e99..2017a39 100644 (file)
@@ -55,7 +55,6 @@ enum KTHREAD_BITS {
        KTHREAD_IS_PER_CPU = 0,
        KTHREAD_SHOULD_STOP,
        KTHREAD_SHOULD_PARK,
-       KTHREAD_IS_PARKED,
 };
 
 static inline void set_kthread_struct(void *kthread)
@@ -177,14 +176,12 @@ void *kthread_probe_data(struct task_struct *task)
 
 static void __kthread_parkme(struct kthread *self)
 {
-       __set_current_state(TASK_PARKED);
-       while (test_bit(KTHREAD_SHOULD_PARK, &self->flags)) {
-               if (!test_and_set_bit(KTHREAD_IS_PARKED, &self->flags))
-                       complete(&self->parked);
+       for (;;) {
+               set_current_state(TASK_PARKED);
+               if (!test_bit(KTHREAD_SHOULD_PARK, &self->flags))
+                       break;
                schedule();
-               __set_current_state(TASK_PARKED);
        }
-       clear_bit(KTHREAD_IS_PARKED, &self->flags);
        __set_current_state(TASK_RUNNING);
 }
 
@@ -194,6 +191,11 @@ void kthread_parkme(void)
 }
 EXPORT_SYMBOL_GPL(kthread_parkme);
 
+void kthread_park_complete(struct task_struct *k)
+{
+       complete(&to_kthread(k)->parked);
+}
+
 static int kthread(void *_create)
 {
        /* Copy data: it's on kthread's stack */
@@ -450,22 +452,15 @@ void kthread_unpark(struct task_struct *k)
 {
        struct kthread *kthread = to_kthread(k);
 
-       clear_bit(KTHREAD_SHOULD_PARK, &kthread->flags);
        /*
-        * We clear the IS_PARKED bit here as we don't wait
-        * until the task has left the park code. So if we'd
-        * park before that happens we'd see the IS_PARKED bit
-        * which might be about to be cleared.
+        * Newly created kthread was parked when the CPU was offline.
+        * The binding was lost and we need to set it again.
         */
-       if (test_and_clear_bit(KTHREAD_IS_PARKED, &kthread->flags)) {
-               /*
-                * Newly created kthread was parked when the CPU was offline.
-                * The binding was lost and we need to set it again.
-                */
-               if (test_bit(KTHREAD_IS_PER_CPU, &kthread->flags))
-                       __kthread_bind(k, kthread->cpu, TASK_PARKED);
-               wake_up_state(k, TASK_PARKED);
-       }
+       if (test_bit(KTHREAD_IS_PER_CPU, &kthread->flags))
+               __kthread_bind(k, kthread->cpu, TASK_PARKED);
+
+       clear_bit(KTHREAD_SHOULD_PARK, &kthread->flags);
+       wake_up_state(k, TASK_PARKED);
 }
 EXPORT_SYMBOL_GPL(kthread_unpark);
 
@@ -488,12 +483,13 @@ int kthread_park(struct task_struct *k)
        if (WARN_ON(k->flags & PF_EXITING))
                return -ENOSYS;
 
-       if (!test_bit(KTHREAD_IS_PARKED, &kthread->flags)) {
-               set_bit(KTHREAD_SHOULD_PARK, &kthread->flags);
-               if (k != current) {
-                       wake_up_process(k);
-                       wait_for_completion(&kthread->parked);
-               }
+       if (WARN_ON_ONCE(test_bit(KTHREAD_SHOULD_PARK, &kthread->flags)))
+               return -EBUSY;
+
+       set_bit(KTHREAD_SHOULD_PARK, &kthread->flags);
+       if (k != current) {
+               wake_up_process(k);
+               wait_for_completion(&kthread->parked);
        }
 
        return 0;
index e795908..a903367 100644 (file)
@@ -352,16 +352,15 @@ static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
        struct task_struct *owner;
        bool ret = true;
 
+       BUILD_BUG_ON(!rwsem_has_anonymous_owner(RWSEM_OWNER_UNKNOWN));
+
        if (need_resched())
                return false;
 
        rcu_read_lock();
        owner = READ_ONCE(sem->owner);
-       if (!rwsem_owner_is_writer(owner)) {
-               /*
-                * Don't spin if the rwsem is readers owned.
-                */
-               ret = !rwsem_owner_is_reader(owner);
+       if (!owner || !is_rwsem_owner_spinnable(owner)) {
+               ret = !owner;   /* !owner is spinnable */
                goto done;
        }
 
@@ -382,11 +381,11 @@ static noinline bool rwsem_spin_on_owner(struct rw_semaphore *sem)
 {
        struct task_struct *owner = READ_ONCE(sem->owner);
 
-       if (!rwsem_owner_is_writer(owner))
-               goto out;
+       if (!is_rwsem_owner_spinnable(owner))
+               return false;
 
        rcu_read_lock();
-       while (sem->owner == owner) {
+       while (owner && (READ_ONCE(sem->owner) == owner)) {
                /*
                 * Ensure we emit the owner->on_cpu, dereference _after_
                 * checking sem->owner still matches owner, if that fails,
@@ -408,12 +407,12 @@ static noinline bool rwsem_spin_on_owner(struct rw_semaphore *sem)
                cpu_relax();
        }
        rcu_read_unlock();
-out:
+
        /*
         * If there is a new owner or the owner is not set, we continue
         * spinning.
         */
-       return !rwsem_owner_is_reader(READ_ONCE(sem->owner));
+       return is_rwsem_owner_spinnable(READ_ONCE(sem->owner));
 }
 
 static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
index 30465a2..bc1e507 100644 (file)
@@ -221,5 +221,3 @@ void up_read_non_owner(struct rw_semaphore *sem)
 EXPORT_SYMBOL(up_read_non_owner);
 
 #endif
-
-
index a17cba8..b9d0e72 100644 (file)
@@ -1,20 +1,24 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
  * The owner field of the rw_semaphore structure will be set to
- * RWSEM_READ_OWNED when a reader grabs the lock. A writer will clear
+ * RWSEM_READER_OWNED when a reader grabs the lock. A writer will clear
  * the owner field when it unlocks. A reader, on the other hand, will
  * not touch the owner field when it unlocks.
  *
- * In essence, the owner field now has the following 3 states:
+ * In essence, the owner field now has the following 4 states:
  *  1) 0
  *     - lock is free or the owner hasn't set the field yet
  *  2) RWSEM_READER_OWNED
  *     - lock is currently or previously owned by readers (lock is free
  *       or not set by owner yet)
- *  3) Other non-zero value
- *     - a writer owns the lock
+ *  3) RWSEM_ANONYMOUSLY_OWNED bit set with some other bits set as well
+ *     - lock is owned by an anonymous writer, so spinning on the lock
+ *       owner should be disabled.
+ *  4) Other non-zero value
+ *     - a writer owns the lock and other writers can spin on the lock owner.
  */
-#define RWSEM_READER_OWNED     ((struct task_struct *)1UL)
+#define RWSEM_ANONYMOUSLY_OWNED        (1UL << 0)
+#define RWSEM_READER_OWNED     ((struct task_struct *)RWSEM_ANONYMOUSLY_OWNED)
 
 #ifdef CONFIG_DEBUG_RWSEMS
 # define DEBUG_RWSEMS_WARN_ON(c)       DEBUG_LOCKS_WARN_ON(c)
@@ -51,14 +55,22 @@ static inline void rwsem_set_reader_owned(struct rw_semaphore *sem)
                WRITE_ONCE(sem->owner, RWSEM_READER_OWNED);
 }
 
-static inline bool rwsem_owner_is_writer(struct task_struct *owner)
+/*
+ * Return true if the a rwsem waiter can spin on the rwsem's owner
+ * and steal the lock, i.e. the lock is not anonymously owned.
+ * N.B. !owner is considered spinnable.
+ */
+static inline bool is_rwsem_owner_spinnable(struct task_struct *owner)
 {
-       return owner && owner != RWSEM_READER_OWNED;
+       return !((unsigned long)owner & RWSEM_ANONYMOUSLY_OWNED);
 }
 
-static inline bool rwsem_owner_is_reader(struct task_struct *owner)
+/*
+ * Return true if rwsem is owned by an anonymous writer or readers.
+ */
+static inline bool rwsem_has_anonymous_owner(struct task_struct *owner)
 {
-       return owner == RWSEM_READER_OWNED;
+       return (unsigned long)owner & RWSEM_ANONYMOUSLY_OWNED;
 }
 #else
 static inline void rwsem_set_owner(struct rw_semaphore *sem)
index ce8066b..c9bea7f 100644 (file)
@@ -3517,6 +3517,11 @@ static noinline int do_init_module(struct module *mod)
         * walking this with preempt disabled.  In all the failure paths, we
         * call synchronize_sched(), but we don't want to slow down the success
         * path, so use actual RCU here.
+        * Note that module_alloc() on most architectures creates W+X page
+        * mappings which won't be cleaned up until do_free_init() runs.  Any
+        * code such as mark_rodata_ro() which depends on those mappings to
+        * be cleaned up needs to sync with the queued work - ie
+        * rcu_barrier_sched()
         */
        call_rcu_sched(&freeinit->rcu, do_free_init);
        mutex_unlock(&module_mutex);
index 6be6c57..2d4ff53 100644 (file)
@@ -2,6 +2,7 @@
 /*
  * Auto-group scheduling implementation:
  */
+#include <linux/nospec.h>
 #include "sched.h"
 
 unsigned int __read_mostly sysctl_sched_autogroup_enabled = 1;
@@ -209,7 +210,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int nice)
        static unsigned long next = INITIAL_JIFFIES;
        struct autogroup *ag;
        unsigned long shares;
-       int err;
+       int err, idx;
 
        if (nice < MIN_NICE || nice > MAX_NICE)
                return -EINVAL;
@@ -227,7 +228,9 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int nice)
 
        next = HZ / 10 + jiffies;
        ag = autogroup_task_get(p);
-       shares = scale_load(sched_prio_to_weight[nice + 20]);
+
+       idx = array_index_nospec(nice + 20, 40);
+       shares = scale_load(sched_prio_to_weight[idx]);
 
        down_write(&ag->lock);
        err = sched_group_set_shares(ag->tg, shares);
index 5e10aae..092f7c4 100644 (file)
@@ -7,6 +7,9 @@
  */
 #include "sched.h"
 
+#include <linux/kthread.h>
+#include <linux/nospec.h>
+
 #include <asm/switch_to.h>
 #include <asm/tlb.h>
 
@@ -2718,20 +2721,28 @@ static struct rq *finish_task_switch(struct task_struct *prev)
                membarrier_mm_sync_core_before_usermode(mm);
                mmdrop(mm);
        }
-       if (unlikely(prev_state == TASK_DEAD)) {
-               if (prev->sched_class->task_dead)
-                       prev->sched_class->task_dead(prev);
+       if (unlikely(prev_state & (TASK_DEAD|TASK_PARKED))) {
+               switch (prev_state) {
+               case TASK_DEAD:
+                       if (prev->sched_class->task_dead)
+                               prev->sched_class->task_dead(prev);
 
-               /*
-                * Remove function-return probe instances associated with this
-                * task and put them back on the free list.
-                */
-               kprobe_flush_task(prev);
+                       /*
+                        * Remove function-return probe instances associated with this
+                        * task and put them back on the free list.
+                        */
+                       kprobe_flush_task(prev);
+
+                       /* Task is done with its stack. */
+                       put_task_stack(prev);
 
-               /* Task is done with its stack. */
-               put_task_stack(prev);
+                       put_task_struct(prev);
+                       break;
 
-               put_task_struct(prev);
+               case TASK_PARKED:
+                       kthread_park_complete(prev);
+                       break;
+               }
        }
 
        tick_nohz_task_switch();
@@ -3498,23 +3509,8 @@ static void __sched notrace __schedule(bool preempt)
 
 void __noreturn do_task_dead(void)
 {
-       /*
-        * The setting of TASK_RUNNING by try_to_wake_up() may be delayed
-        * when the following two conditions become true.
-        *   - There is race condition of mmap_sem (It is acquired by
-        *     exit_mm()), and
-        *   - SMI occurs before setting TASK_RUNINNG.
-        *     (or hypervisor of virtual machine switches to other guest)
-        *  As a result, we may become TASK_RUNNING after becoming TASK_DEAD
-        *
-        * To avoid it, we have to wait for releasing tsk->pi_lock which
-        * is held by try_to_wake_up()
-        */
-       raw_spin_lock_irq(&current->pi_lock);
-       raw_spin_unlock_irq(&current->pi_lock);
-
        /* Causes final put_task_struct in finish_task_switch(): */
-       __set_current_state(TASK_DEAD);
+       set_special_state(TASK_DEAD);
 
        /* Tell freezer to ignore us: */
        current->flags |= PF_NOFREEZE;
@@ -6928,11 +6924,15 @@ static int cpu_weight_nice_write_s64(struct cgroup_subsys_state *css,
                                     struct cftype *cft, s64 nice)
 {
        unsigned long weight;
+       int idx;
 
        if (nice < MIN_NICE || nice > MAX_NICE)
                return -ERANGE;
 
-       weight = sched_prio_to_weight[NICE_TO_PRIO(nice) - MAX_RT_PRIO];
+       idx = NICE_TO_PRIO(nice) - MAX_RT_PRIO;
+       idx = array_index_nospec(idx, 40);
+       weight = sched_prio_to_weight[idx];
+
        return sched_group_set_shares(css_tg(css), scale_load(weight));
 }
 #endif
index e7b3008..1356afd 100644 (file)
@@ -1117,7 +1117,7 @@ extern bool sched_rt_bandwidth_account(struct rt_rq *rt_rq);
  * should be larger than 2^(64 - 20 - 8), which is more than 64 seconds.
  * So, overflow is not an issue here.
  */
-u64 grub_reclaim(u64 delta, struct rq *rq, struct sched_dl_entity *dl_se)
+static u64 grub_reclaim(u64 delta, struct rq *rq, struct sched_dl_entity *dl_se)
 {
        u64 u_inact = rq->dl.this_bw - rq->dl.running_bw; /* Utot - Uact */
        u64 u_act;
@@ -2731,8 +2731,6 @@ bool dl_cpu_busy(unsigned int cpu)
 #endif
 
 #ifdef CONFIG_SCHED_DEBUG
-extern void print_dl_rq(struct seq_file *m, int cpu, struct dl_rq *dl_rq);
-
 void print_dl_stats(struct seq_file *m, int cpu)
 {
        print_dl_rq(m, cpu, &cpu_rq(cpu)->dl);
index 54dc31e..79f574d 100644 (file)
@@ -1854,7 +1854,6 @@ static int task_numa_migrate(struct task_struct *p)
 static void numa_migrate_preferred(struct task_struct *p)
 {
        unsigned long interval = HZ;
-       unsigned long numa_migrate_retry;
 
        /* This task has no NUMA fault statistics yet */
        if (unlikely(p->numa_preferred_nid == -1 || !p->numa_faults))
@@ -1862,18 +1861,7 @@ static void numa_migrate_preferred(struct task_struct *p)
 
        /* Periodically retry migrating the task to the preferred node */
        interval = min(interval, msecs_to_jiffies(p->numa_scan_period) / 16);
-       numa_migrate_retry = jiffies + interval;
-
-       /*
-        * Check that the new retry threshold is after the current one. If
-        * the retry is in the future, it implies that wake_affine has
-        * temporarily asked NUMA balancing to backoff from placement.
-        */
-       if (numa_migrate_retry > p->numa_migrate_retry)
-               return;
-
-       /* Safe to try placing the task on the preferred node */
-       p->numa_migrate_retry = numa_migrate_retry;
+       p->numa_migrate_retry = jiffies + interval;
 
        /* Success if task is already running on preferred CPU */
        if (task_node(p) == p->numa_preferred_nid)
@@ -5922,48 +5910,6 @@ wake_affine_weight(struct sched_domain *sd, struct task_struct *p,
        return this_eff_load < prev_eff_load ? this_cpu : nr_cpumask_bits;
 }
 
-#ifdef CONFIG_NUMA_BALANCING
-static void
-update_wa_numa_placement(struct task_struct *p, int prev_cpu, int target)
-{
-       unsigned long interval;
-
-       if (!static_branch_likely(&sched_numa_balancing))
-               return;
-
-       /* If balancing has no preference then continue gathering data */
-       if (p->numa_preferred_nid == -1)
-               return;
-
-       /*
-        * If the wakeup is not affecting locality then it is neutral from
-        * the perspective of NUMA balacing so continue gathering data.
-        */
-       if (cpu_to_node(prev_cpu) == cpu_to_node(target))
-               return;
-
-       /*
-        * Temporarily prevent NUMA balancing trying to place waker/wakee after
-        * wakee has been moved by wake_affine. This will potentially allow
-        * related tasks to converge and update their data placement. The
-        * 4 * numa_scan_period is to allow the two-pass filter to migrate
-        * hot data to the wakers node.
-        */
-       interval = max(sysctl_numa_balancing_scan_delay,
-                        p->numa_scan_period << 2);
-       p->numa_migrate_retry = jiffies + msecs_to_jiffies(interval);
-
-       interval = max(sysctl_numa_balancing_scan_delay,
-                        current->numa_scan_period << 2);
-       current->numa_migrate_retry = jiffies + msecs_to_jiffies(interval);
-}
-#else
-static void
-update_wa_numa_placement(struct task_struct *p, int prev_cpu, int target)
-{
-}
-#endif
-
 static int wake_affine(struct sched_domain *sd, struct task_struct *p,
                       int this_cpu, int prev_cpu, int sync)
 {
@@ -5979,7 +5925,6 @@ static int wake_affine(struct sched_domain *sd, struct task_struct *p,
        if (target == nr_cpumask_bits)
                return prev_cpu;
 
-       update_wa_numa_placement(p, prev_cpu, target);
        schedstat_inc(sd->ttwu_move_affine);
        schedstat_inc(p->se.statistics.nr_wakeups_affine);
        return target;
@@ -9847,6 +9792,7 @@ static int idle_balance(struct rq *this_rq, struct rq_flags *rf)
        if (curr_cost > this_rq->max_idle_balance_cost)
                this_rq->max_idle_balance_cost = curr_cost;
 
+out:
        /*
         * While browsing the domains, we released the rq lock, a task could
         * have been enqueued in the meantime. Since we're not going idle,
@@ -9855,7 +9801,6 @@ static int idle_balance(struct rq *this_rq, struct rq_flags *rf)
        if (this_rq->cfs.h_nr_running && !pulled_task)
                pulled_task = 1;
 
-out:
        /* Move the next balance forward */
        if (time_after(this_rq->next_balance, next_balance))
                this_rq->next_balance = next_balance;
index 7aef6b4..ef3c4e6 100644 (file)
@@ -2701,8 +2701,6 @@ int sched_rr_handler(struct ctl_table *table, int write,
 }
 
 #ifdef CONFIG_SCHED_DEBUG
-extern void print_rt_rq(struct seq_file *m, int cpu, struct rt_rq *rt_rq);
-
 void print_rt_stats(struct seq_file *m, int cpu)
 {
        rt_rq_iter_t iter;
index 15750c2..1f0a4bc 100644 (file)
@@ -2025,8 +2025,9 @@ extern bool sched_debug_enabled;
 extern void print_cfs_stats(struct seq_file *m, int cpu);
 extern void print_rt_stats(struct seq_file *m, int cpu);
 extern void print_dl_stats(struct seq_file *m, int cpu);
-extern void
-print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq);
+extern void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq);
+extern void print_rt_rq(struct seq_file *m, int cpu, struct rt_rq *rt_rq);
+extern void print_dl_rq(struct seq_file *m, int cpu, struct dl_rq *dl_rq);
 #ifdef CONFIG_NUMA_BALANCING
 extern void
 show_numa_stats(struct task_struct *p, struct seq_file *m);
index d4ccea5..9c33163 100644 (file)
@@ -1961,14 +1961,27 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
                        return;
        }
 
+       set_special_state(TASK_TRACED);
+
        /*
         * We're committing to trapping.  TRACED should be visible before
         * TRAPPING is cleared; otherwise, the tracer might fail do_wait().
         * Also, transition to TRACED and updates to ->jobctl should be
         * atomic with respect to siglock and should be done after the arch
         * hook as siglock is released and regrabbed across it.
+        *
+        *     TRACER                               TRACEE
+        *
+        *     ptrace_attach()
+        * [L]   wait_on_bit(JOBCTL_TRAPPING)   [S] set_special_state(TRACED)
+        *     do_wait()
+        *       set_current_state()                smp_wmb();
+        *       ptrace_do_wait()
+        *         wait_task_stopped()
+        *           task_stopped_code()
+        * [L]         task_is_traced()         [S] task_clear_jobctl_trapping();
         */
-       set_current_state(TASK_TRACED);
+       smp_wmb();
 
        current->last_siginfo = info;
        current->exit_code = exit_code;
@@ -2176,7 +2189,7 @@ static bool do_signal_stop(int signr)
                if (task_participate_group_stop(current))
                        notify = CLD_STOPPED;
 
-               __set_current_state(TASK_STOPPED);
+               set_special_state(TASK_STOPPED);
                spin_unlock_irq(&current->sighand->siglock);
 
                /*
index b759126..64c0291 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/smpboot.h>
 #include <linux/atomic.h>
 #include <linux/nmi.h>
+#include <linux/sched/wake_q.h>
 
 /*
  * Structure to determine completion condition and record errors.  May
@@ -65,27 +66,31 @@ static void cpu_stop_signal_done(struct cpu_stop_done *done)
 }
 
 static void __cpu_stop_queue_work(struct cpu_stopper *stopper,
-                                       struct cpu_stop_work *work)
+                                       struct cpu_stop_work *work,
+                                       struct wake_q_head *wakeq)
 {
        list_add_tail(&work->list, &stopper->works);
-       wake_up_process(stopper->thread);
+       wake_q_add(wakeq, stopper->thread);
 }
 
 /* queue @work to @stopper.  if offline, @work is completed immediately */
 static bool cpu_stop_queue_work(unsigned int cpu, struct cpu_stop_work *work)
 {
        struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu);
+       DEFINE_WAKE_Q(wakeq);
        unsigned long flags;
        bool enabled;
 
        spin_lock_irqsave(&stopper->lock, flags);
        enabled = stopper->enabled;
        if (enabled)
-               __cpu_stop_queue_work(stopper, work);
+               __cpu_stop_queue_work(stopper, work, &wakeq);
        else if (work->done)
                cpu_stop_signal_done(work->done);
        spin_unlock_irqrestore(&stopper->lock, flags);
 
+       wake_up_q(&wakeq);
+
        return enabled;
 }
 
@@ -229,6 +234,7 @@ static int cpu_stop_queue_two_works(int cpu1, struct cpu_stop_work *work1,
 {
        struct cpu_stopper *stopper1 = per_cpu_ptr(&cpu_stopper, cpu1);
        struct cpu_stopper *stopper2 = per_cpu_ptr(&cpu_stopper, cpu2);
+       DEFINE_WAKE_Q(wakeq);
        int err;
 retry:
        spin_lock_irq(&stopper1->lock);
@@ -252,8 +258,8 @@ retry:
                        goto unlock;
 
        err = 0;
-       __cpu_stop_queue_work(stopper1, work1);
-       __cpu_stop_queue_work(stopper2, work2);
+       __cpu_stop_queue_work(stopper1, work1, &wakeq);
+       __cpu_stop_queue_work(stopper2, work2, &wakeq);
 unlock:
        spin_unlock(&stopper2->lock);
        spin_unlock_irq(&stopper1->lock);
@@ -263,6 +269,9 @@ unlock:
                        cpu_relax();
                goto retry;
        }
+
+       wake_up_q(&wakeq);
+
        return err;
 }
 /**
index b398c2e..aa2094d 100644 (file)
@@ -612,6 +612,14 @@ static void tick_handle_oneshot_broadcast(struct clock_event_device *dev)
        now = ktime_get();
        /* Find all expired events */
        for_each_cpu(cpu, tick_broadcast_oneshot_mask) {
+               /*
+                * Required for !SMP because for_each_cpu() reports
+                * unconditionally CPU0 as set on UP kernels.
+                */
+               if (!IS_ENABLED(CONFIG_SMP) &&
+                   cpumask_empty(tick_broadcast_oneshot_mask))
+                       break;
+
                td = &per_cpu(tick_cpu_device, cpu);
                if (td->evtdev->next_event <= now) {
                        cpumask_set_cpu(cpu, tmpmask);
index f76b3ff..30db93f 100644 (file)
@@ -25,6 +25,8 @@
 #include <linux/ptrace.h>
 #include <linux/async.h>
 #include <linux/uaccess.h>
+#include <linux/shmem_fs.h>
+#include <linux/pipe_fs_i.h>
 
 #include <trace/events/module.h>
 
@@ -97,9 +99,13 @@ static int call_usermodehelper_exec_async(void *data)
 
        commit_creds(new);
 
-       retval = do_execve(getname_kernel(sub_info->path),
-                          (const char __user *const __user *)sub_info->argv,
-                          (const char __user *const __user *)sub_info->envp);
+       if (sub_info->file)
+               retval = do_execve_file(sub_info->file,
+                                       sub_info->argv, sub_info->envp);
+       else
+               retval = do_execve(getname_kernel(sub_info->path),
+                                  (const char __user *const __user *)sub_info->argv,
+                                  (const char __user *const __user *)sub_info->envp);
 out:
        sub_info->retval = retval;
        /*
@@ -185,6 +191,8 @@ static void call_usermodehelper_exec_work(struct work_struct *work)
                if (pid < 0) {
                        sub_info->retval = pid;
                        umh_complete(sub_info);
+               } else {
+                       sub_info->pid = pid;
                }
        }
 }
@@ -393,6 +401,117 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
 }
 EXPORT_SYMBOL(call_usermodehelper_setup);
 
+struct subprocess_info *call_usermodehelper_setup_file(struct file *file,
+               int (*init)(struct subprocess_info *info, struct cred *new),
+               void (*cleanup)(struct subprocess_info *info), void *data)
+{
+       struct subprocess_info *sub_info;
+
+       sub_info = kzalloc(sizeof(struct subprocess_info), GFP_KERNEL);
+       if (!sub_info)
+               return NULL;
+
+       INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);
+       sub_info->path = "none";
+       sub_info->file = file;
+       sub_info->init = init;
+       sub_info->cleanup = cleanup;
+       sub_info->data = data;
+       return sub_info;
+}
+
+static int umh_pipe_setup(struct subprocess_info *info, struct cred *new)
+{
+       struct umh_info *umh_info = info->data;
+       struct file *from_umh[2];
+       struct file *to_umh[2];
+       int err;
+
+       /* create pipe to send data to umh */
+       err = create_pipe_files(to_umh, 0);
+       if (err)
+               return err;
+       err = replace_fd(0, to_umh[0], 0);
+       fput(to_umh[0]);
+       if (err < 0) {
+               fput(to_umh[1]);
+               return err;
+       }
+
+       /* create pipe to receive data from umh */
+       err = create_pipe_files(from_umh, 0);
+       if (err) {
+               fput(to_umh[1]);
+               replace_fd(0, NULL, 0);
+               return err;
+       }
+       err = replace_fd(1, from_umh[1], 0);
+       fput(from_umh[1]);
+       if (err < 0) {
+               fput(to_umh[1]);
+               replace_fd(0, NULL, 0);
+               fput(from_umh[0]);
+               return err;
+       }
+
+       umh_info->pipe_to_umh = to_umh[1];
+       umh_info->pipe_from_umh = from_umh[0];
+       return 0;
+}
+
+static void umh_save_pid(struct subprocess_info *info)
+{
+       struct umh_info *umh_info = info->data;
+
+       umh_info->pid = info->pid;
+}
+
+/**
+ * fork_usermode_blob - fork a blob of bytes as a usermode process
+ * @data: a blob of bytes that can be do_execv-ed as a file
+ * @len: length of the blob
+ * @info: information about usermode process (shouldn't be NULL)
+ *
+ * Returns either negative error or zero which indicates success
+ * in executing a blob of bytes as a usermode process. In such
+ * case 'struct umh_info *info' is populated with two pipes
+ * and a pid of the process. The caller is responsible for health
+ * check of the user process, killing it via pid, and closing the
+ * pipes when user process is no longer needed.
+ */
+int fork_usermode_blob(void *data, size_t len, struct umh_info *info)
+{
+       struct subprocess_info *sub_info;
+       struct file *file;
+       ssize_t written;
+       loff_t pos = 0;
+       int err;
+
+       file = shmem_kernel_file_setup("", len, 0);
+       if (IS_ERR(file))
+               return PTR_ERR(file);
+
+       written = kernel_write(file, data, len, &pos);
+       if (written != len) {
+               err = written;
+               if (err >= 0)
+                       err = -ENOMEM;
+               goto out;
+       }
+
+       err = -ENOMEM;
+       sub_info = call_usermodehelper_setup_file(file, umh_pipe_setup,
+                                                 umh_save_pid, info);
+       if (!sub_info)
+               goto out;
+
+       err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
+out:
+       fput(file);
+       return err;
+}
+EXPORT_SYMBOL_GPL(fork_usermode_blob);
+
 /**
  * call_usermodehelper_exec - start a usermode application
  * @sub_info: information about the subprocessa
index 5985a25..5367ffa 100644 (file)
@@ -132,7 +132,12 @@ static int __init find_bit_test(void)
        test_find_next_bit(bitmap, BITMAP_LEN);
        test_find_next_zero_bit(bitmap, BITMAP_LEN);
        test_find_last_bit(bitmap, BITMAP_LEN);
-       test_find_first_bit(bitmap, BITMAP_LEN);
+
+       /*
+        * test_find_first_bit() may take some time, so
+        * traverse only part of bitmap to avoid soft lockup.
+        */
+       test_find_first_bit(bitmap, BITMAP_LEN / 10);
        test_find_next_and_bit(bitmap, bitmap2, BITMAP_LEN);
 
        pr_err("\nStart testing find_bit() with sparse bitmap\n");
index da9e10c..43e0cbe 100644 (file)
@@ -1612,11 +1612,9 @@ static void set_iter_tags(struct radix_tree_iter *iter,
 static void __rcu **skip_siblings(struct radix_tree_node **nodep,
                        void __rcu **slot, struct radix_tree_iter *iter)
 {
-       void *sib = node_to_entry(slot - 1);
-
        while (iter->index < iter->next_index) {
                *nodep = rcu_dereference_raw(*slot);
-               if (*nodep && *nodep != sib)
+               if (*nodep && !is_sibling_entry(iter->node, *nodep))
                        return slot;
                slot++;
                iter->index = __radix_tree_iter_add(iter, 1);
@@ -1631,7 +1629,7 @@ void __rcu **__radix_tree_next_slot(void __rcu **slot,
                                struct radix_tree_iter *iter, unsigned flags)
 {
        unsigned tag = flags & RADIX_TREE_ITER_TAG_MASK;
-       struct radix_tree_node *node = rcu_dereference_raw(*slot);
+       struct radix_tree_node *node;
 
        slot = skip_siblings(&node, slot, iter);
 
index 12fbaa4..cc64058 100644 (file)
@@ -714,7 +714,7 @@ swiotlb_alloc_buffer(struct device *dev, size_t size, dma_addr_t *dma_handle,
 
        phys_addr = swiotlb_tbl_map_single(dev,
                        __phys_to_dma(dev, io_tlb_start),
-                       0, size, DMA_FROM_DEVICE, 0);
+                       0, size, DMA_FROM_DEVICE, attrs);
        if (phys_addr == SWIOTLB_MAP_ERROR)
                goto out_warn;
 
index de16f78..6cd7d07 100644 (file)
@@ -331,23 +331,32 @@ static void noinline __init test_mem_optimisations(void)
        unsigned int start, nbits;
 
        for (start = 0; start < 1024; start += 8) {
-               memset(bmap1, 0x5a, sizeof(bmap1));
-               memset(bmap2, 0x5a, sizeof(bmap2));
                for (nbits = 0; nbits < 1024 - start; nbits += 8) {
+                       memset(bmap1, 0x5a, sizeof(bmap1));
+                       memset(bmap2, 0x5a, sizeof(bmap2));
+
                        bitmap_set(bmap1, start, nbits);
                        __bitmap_set(bmap2, start, nbits);
-                       if (!bitmap_equal(bmap1, bmap2, 1024))
+                       if (!bitmap_equal(bmap1, bmap2, 1024)) {
                                printk("set not equal %d %d\n", start, nbits);
-                       if (!__bitmap_equal(bmap1, bmap2, 1024))
+                               failed_tests++;
+                       }
+                       if (!__bitmap_equal(bmap1, bmap2, 1024)) {
                                printk("set not __equal %d %d\n", start, nbits);
+                               failed_tests++;
+                       }
 
                        bitmap_clear(bmap1, start, nbits);
                        __bitmap_clear(bmap2, start, nbits);
-                       if (!bitmap_equal(bmap1, bmap2, 1024))
+                       if (!bitmap_equal(bmap1, bmap2, 1024)) {
                                printk("clear not equal %d %d\n", start, nbits);
-                       if (!__bitmap_equal(bmap1, bmap2, 1024))
+                               failed_tests++;
+                       }
+                       if (!__bitmap_equal(bmap1, bmap2, 1024)) {
                                printk("clear not __equal %d %d\n", start,
                                                                        nbits);
+                               failed_tests++;
+                       }
                }
        }
 }
index 30c0cb8..23920c5 100644 (file)
@@ -1669,19 +1669,22 @@ char *pointer_string(char *buf, char *end, const void *ptr,
        return number(buf, end, (unsigned long int)ptr, spec);
 }
 
-static bool have_filled_random_ptr_key __read_mostly;
+static DEFINE_STATIC_KEY_TRUE(not_filled_random_ptr_key);
 static siphash_key_t ptr_key __read_mostly;
 
-static void fill_random_ptr_key(struct random_ready_callback *unused)
+static void enable_ptr_key_workfn(struct work_struct *work)
 {
        get_random_bytes(&ptr_key, sizeof(ptr_key));
-       /*
-        * have_filled_random_ptr_key==true is dependent on get_random_bytes().
-        * ptr_to_id() needs to see have_filled_random_ptr_key==true
-        * after get_random_bytes() returns.
-        */
-       smp_mb();
-       WRITE_ONCE(have_filled_random_ptr_key, true);
+       /* Needs to run from preemptible context */
+       static_branch_disable(&not_filled_random_ptr_key);
+}
+
+static DECLARE_WORK(enable_ptr_key_work, enable_ptr_key_workfn);
+
+static void fill_random_ptr_key(struct random_ready_callback *unused)
+{
+       /* This may be in an interrupt handler. */
+       queue_work(system_unbound_wq, &enable_ptr_key_work);
 }
 
 static struct random_ready_callback random_ready = {
@@ -1695,7 +1698,8 @@ static int __init initialize_ptr_random(void)
        if (!ret) {
                return 0;
        } else if (ret == -EALREADY) {
-               fill_random_ptr_key(&random_ready);
+               /* This is in preemptible context */
+               enable_ptr_key_workfn(&enable_ptr_key_work);
                return 0;
        }
 
@@ -1709,7 +1713,7 @@ static char *ptr_to_id(char *buf, char *end, void *ptr, struct printf_spec spec)
        unsigned long hashval;
        const int default_width = 2 * sizeof(ptr);
 
-       if (unlikely(!have_filled_random_ptr_key)) {
+       if (static_branch_unlikely(&not_filled_random_ptr_key)) {
                spec.field_width = default_width;
                /* string length must be less than default_width */
                return string(buf, end, "(ptrval)", spec);
index d5004d8..e14c015 100644 (file)
@@ -636,6 +636,7 @@ config DEFERRED_STRUCT_PAGE_INIT
        default n
        depends on NO_BOOTMEM
        depends on !FLATMEM
+       depends on !NEED_PER_CPU_KM
        help
          Ordinarily all struct pages are initialised during early boot in a
          single thread. On very large machines this can take a considerable
index 76af4cf..541904a 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -544,6 +544,9 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)
        if (vm_flags & (VM_IO | VM_PFNMAP))
                return -EFAULT;
 
+       if (gup_flags & FOLL_ANON && !vma_is_anonymous(vma))
+               return -EFAULT;
+
        if (write) {
                if (!(vm_flags & VM_WRITE)) {
                        if (!(gup_flags & FOLL_FORCE))
index 5684330..8c0af0f 100644 (file)
@@ -528,14 +528,12 @@ int migrate_page_move_mapping(struct address_space *mapping,
                int i;
                int index = page_index(page);
 
-               for (i = 0; i < HPAGE_PMD_NR; i++) {
+               for (i = 1; i < HPAGE_PMD_NR; i++) {
                        pslot = radix_tree_lookup_slot(&mapping->i_pages,
                                                       index + i);
                        radix_tree_replace_slot(&mapping->i_pages, pslot,
                                                newpage + i);
                }
-       } else {
-               radix_tree_replace_slot(&mapping->i_pages, pslot, newpage);
        }
 
        /*
index 6fc4357..fc41c05 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1327,7 +1327,7 @@ static inline int mlock_future_check(struct mm_struct *mm,
 static inline u64 file_mmap_size_max(struct file *file, struct inode *inode)
 {
        if (S_ISREG(inode->i_mode))
-               return inode->i_sb->s_maxbytes;
+               return MAX_LFS_FILESIZE;
 
        if (S_ISBLK(inode->i_mode))
                return MAX_LFS_FILESIZE;
@@ -3056,6 +3056,32 @@ void exit_mmap(struct mm_struct *mm)
        /* mm's last user has gone, and its about to be pulled down */
        mmu_notifier_release(mm);
 
+       if (unlikely(mm_is_oom_victim(mm))) {
+               /*
+                * Manually reap the mm to free as much memory as possible.
+                * Then, as the oom reaper does, set MMF_OOM_SKIP to disregard
+                * this mm from further consideration.  Taking mm->mmap_sem for
+                * write after setting MMF_OOM_SKIP will guarantee that the oom
+                * reaper will not run on this mm again after mmap_sem is
+                * dropped.
+                *
+                * Nothing can be holding mm->mmap_sem here and the above call
+                * to mmu_notifier_release(mm) ensures mmu notifier callbacks in
+                * __oom_reap_task_mm() will not block.
+                *
+                * This needs to be done before calling munlock_vma_pages_all(),
+                * which clears VM_LOCKED, otherwise the oom reaper cannot
+                * reliably test it.
+                */
+               mutex_lock(&oom_lock);
+               __oom_reap_task_mm(mm);
+               mutex_unlock(&oom_lock);
+
+               set_bit(MMF_OOM_SKIP, &mm->flags);
+               down_write(&mm->mmap_sem);
+               up_write(&mm->mmap_sem);
+       }
+
        if (mm->locked_vm) {
                vma = mm->mmap;
                while (vma) {
@@ -3077,24 +3103,6 @@ void exit_mmap(struct mm_struct *mm)
        /* update_hiwater_rss(mm) here? but nobody should be looking */
        /* Use -1 here to ensure all VMAs in the mm are unmapped */
        unmap_vmas(&tlb, vma, 0, -1);
-
-       if (unlikely(mm_is_oom_victim(mm))) {
-               /*
-                * Wait for oom_reap_task() to stop working on this
-                * mm. Because MMF_OOM_SKIP is already set before
-                * calling down_read(), oom_reap_task() will not run
-                * on this "mm" post up_write().
-                *
-                * mm_is_oom_victim() cannot be set from under us
-                * either because victim->mm is already set to NULL
-                * under task_lock before calling mmput and oom_mm is
-                * set not NULL by the OOM killer only if victim->mm
-                * is found not NULL while holding the task_lock.
-                */
-               set_bit(MMF_OOM_SKIP, &mm->flags);
-               down_write(&mm->mmap_sem);
-               up_write(&mm->mmap_sem);
-       }
        free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, USER_PGTABLES_CEILING);
        tlb_finish_mmu(&tlb, 0, -1);
 
index ff992fa..8ba6cb8 100644 (file)
@@ -469,7 +469,6 @@ bool process_shares_mm(struct task_struct *p, struct mm_struct *mm)
        return false;
 }
 
-
 #ifdef CONFIG_MMU
 /*
  * OOM Reaper kernel thread which tries to reap the memory used by the OOM
@@ -480,16 +479,54 @@ static DECLARE_WAIT_QUEUE_HEAD(oom_reaper_wait);
 static struct task_struct *oom_reaper_list;
 static DEFINE_SPINLOCK(oom_reaper_lock);
 
-static bool __oom_reap_task_mm(struct task_struct *tsk, struct mm_struct *mm)
+void __oom_reap_task_mm(struct mm_struct *mm)
 {
-       struct mmu_gather tlb;
        struct vm_area_struct *vma;
+
+       /*
+        * Tell all users of get_user/copy_from_user etc... that the content
+        * is no longer stable. No barriers really needed because unmapping
+        * should imply barriers already and the reader would hit a page fault
+        * if it stumbled over a reaped memory.
+        */
+       set_bit(MMF_UNSTABLE, &mm->flags);
+
+       for (vma = mm->mmap ; vma; vma = vma->vm_next) {
+               if (!can_madv_dontneed_vma(vma))
+                       continue;
+
+               /*
+                * Only anonymous pages have a good chance to be dropped
+                * without additional steps which we cannot afford as we
+                * are OOM already.
+                *
+                * We do not even care about fs backed pages because all
+                * which are reclaimable have already been reclaimed and
+                * we do not want to block exit_mmap by keeping mm ref
+                * count elevated without a good reason.
+                */
+               if (vma_is_anonymous(vma) || !(vma->vm_flags & VM_SHARED)) {
+                       const unsigned long start = vma->vm_start;
+                       const unsigned long end = vma->vm_end;
+                       struct mmu_gather tlb;
+
+                       tlb_gather_mmu(&tlb, mm, start, end);
+                       mmu_notifier_invalidate_range_start(mm, start, end);
+                       unmap_page_range(&tlb, vma, start, end, NULL);
+                       mmu_notifier_invalidate_range_end(mm, start, end);
+                       tlb_finish_mmu(&tlb, start, end);
+               }
+       }
+}
+
+static bool oom_reap_task_mm(struct task_struct *tsk, struct mm_struct *mm)
+{
        bool ret = true;
 
        /*
         * We have to make sure to not race with the victim exit path
         * and cause premature new oom victim selection:
-        * __oom_reap_task_mm           exit_mm
+        * oom_reap_task_mm             exit_mm
         *   mmget_not_zero
         *                                mmput
         *                                  atomic_dec_and_test
@@ -534,39 +571,8 @@ static bool __oom_reap_task_mm(struct task_struct *tsk, struct mm_struct *mm)
 
        trace_start_task_reaping(tsk->pid);
 
-       /*
-        * Tell all users of get_user/copy_from_user etc... that the content
-        * is no longer stable. No barriers really needed because unmapping
-        * should imply barriers already and the reader would hit a page fault
-        * if it stumbled over a reaped memory.
-        */
-       set_bit(MMF_UNSTABLE, &mm->flags);
-
-       for (vma = mm->mmap ; vma; vma = vma->vm_next) {
-               if (!can_madv_dontneed_vma(vma))
-                       continue;
+       __oom_reap_task_mm(mm);
 
-               /*
-                * Only anonymous pages have a good chance to be dropped
-                * without additional steps which we cannot afford as we
-                * are OOM already.
-                *
-                * We do not even care about fs backed pages because all
-                * which are reclaimable have already been reclaimed and
-                * we do not want to block exit_mmap by keeping mm ref
-                * count elevated without a good reason.
-                */
-               if (vma_is_anonymous(vma) || !(vma->vm_flags & VM_SHARED)) {
-                       const unsigned long start = vma->vm_start;
-                       const unsigned long end = vma->vm_end;
-
-                       tlb_gather_mmu(&tlb, mm, start, end);
-                       mmu_notifier_invalidate_range_start(mm, start, end);
-                       unmap_page_range(&tlb, vma, start, end, NULL);
-                       mmu_notifier_invalidate_range_end(mm, start, end);
-                       tlb_finish_mmu(&tlb, start, end);
-               }
-       }
        pr_info("oom_reaper: reaped process %d (%s), now anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB\n",
                        task_pid_nr(tsk), tsk->comm,
                        K(get_mm_counter(mm, MM_ANONPAGES)),
@@ -587,14 +593,13 @@ static void oom_reap_task(struct task_struct *tsk)
        struct mm_struct *mm = tsk->signal->oom_mm;
 
        /* Retry the down_read_trylock(mmap_sem) a few times */
-       while (attempts++ < MAX_OOM_REAP_RETRIES && !__oom_reap_task_mm(tsk, mm))
+       while (attempts++ < MAX_OOM_REAP_RETRIES && !oom_reap_task_mm(tsk, mm))
                schedule_timeout_idle(HZ/10);
 
        if (attempts <= MAX_OOM_REAP_RETRIES ||
            test_bit(MMF_OOM_SKIP, &mm->flags))
                goto done;
 
-
        pr_info("oom_reaper: unable to reap pid:%d (%s)\n",
                task_pid_nr(tsk), tsk->comm);
        debug_show_all_locks();
index 62eef26..73dc2fc 100644 (file)
@@ -629,7 +629,7 @@ void offline_mem_sections(unsigned long start_pfn, unsigned long end_pfn)
        unsigned long pfn;
 
        for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
-               unsigned long section_nr = pfn_to_section_nr(start_pfn);
+               unsigned long section_nr = pfn_to_section_nr(pfn);
                struct mem_section *ms;
 
                /*
index 536332e..a2b9518 100644 (file)
@@ -1161,7 +1161,7 @@ const char * const vmstat_text[] = {
        "nr_vmscan_immediate_reclaim",
        "nr_dirtied",
        "nr_written",
-       "nr_indirectly_reclaimable",
+       "", /* nr_indirectly_reclaimable */
 
        /* enum writeback_stat_item counters */
        "nr_dirty_threshold",
@@ -1740,6 +1740,10 @@ static int vmstat_show(struct seq_file *m, void *arg)
        unsigned long *l = arg;
        unsigned long off = l - (unsigned long *)m->private;
 
+       /* Skip hidden vmstat items. */
+       if (*vmstat_text[off] == '\0')
+               return 0;
+
        seq_puts(m, vmstat_text[off]);
        seq_put_decimal_ull(m, " ", *l);
        seq_putc(m, '\n');
index c0bca61..4b366d1 100644 (file)
@@ -144,7 +144,8 @@ enum z3fold_page_flags {
        PAGE_HEADLESS = 0,
        MIDDLE_CHUNK_MAPPED,
        NEEDS_COMPACTING,
-       PAGE_STALE
+       PAGE_STALE,
+       UNDER_RECLAIM
 };
 
 /*****************
@@ -173,6 +174,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);
 
        spin_lock_init(&zhdr->page_lock);
        kref_init(&zhdr->refcount);
@@ -756,6 +758,10 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle)
                atomic64_dec(&pool->pages_nr);
                return;
        }
+       if (test_bit(UNDER_RECLAIM, &page->private)) {
+               z3fold_page_unlock(zhdr);
+               return;
+       }
        if (test_and_set_bit(NEEDS_COMPACTING, &page->private)) {
                z3fold_page_unlock(zhdr);
                return;
@@ -840,6 +846,8 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
                        kref_get(&zhdr->refcount);
                        list_del_init(&zhdr->buddy);
                        zhdr->cpu = -1;
+                       set_bit(UNDER_RECLAIM, &page->private);
+                       break;
                }
 
                list_del_init(&page->lru);
@@ -887,25 +895,35 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries)
                                goto next;
                }
 next:
-               spin_lock(&pool->lock);
                if (test_bit(PAGE_HEADLESS, &page->private)) {
                        if (ret == 0) {
-                               spin_unlock(&pool->lock);
                                free_z3fold_page(page);
                                return 0;
                        }
-               } else if (kref_put(&zhdr->refcount, release_z3fold_page)) {
-                       atomic64_dec(&pool->pages_nr);
+                       spin_lock(&pool->lock);
+                       list_add(&page->lru, &pool->lru);
+                       spin_unlock(&pool->lock);
+               } else {
+                       z3fold_page_lock(zhdr);
+                       clear_bit(UNDER_RECLAIM, &page->private);
+                       if (kref_put(&zhdr->refcount,
+                                       release_z3fold_page_locked)) {
+                               atomic64_dec(&pool->pages_nr);
+                               return 0;
+                       }
+                       /*
+                        * if we are here, the page is still not completely
+                        * free. Take the global pool lock then to be able
+                        * to add it back to the lru list
+                        */
+                       spin_lock(&pool->lock);
+                       list_add(&page->lru, &pool->lru);
                        spin_unlock(&pool->lock);
-                       return 0;
+                       z3fold_page_unlock(zhdr);
                }
 
-               /*
-                * Add to the beginning of LRU.
-                * Pool lock has to be kept here to ensure the page has
-                * not already been released
-                */
-               list_add(&page->lru, &pool->lru);
+               /* We started off locked to we need to lock the pool back */
+               spin_lock(&pool->lock);
        }
        spin_unlock(&pool->lock);
        return -EAGAIN;
index 5505ee6..73a6578 100644 (file)
@@ -118,17 +118,21 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
 }
 
 int vlan_check_real_dev(struct net_device *real_dev,
-                       __be16 protocol, u16 vlan_id)
+                       __be16 protocol, u16 vlan_id,
+                       struct netlink_ext_ack *extack)
 {
        const char *name = real_dev->name;
 
        if (real_dev->features & NETIF_F_VLAN_CHALLENGED) {
                pr_info("VLANs not supported on %s\n", name);
+               NL_SET_ERR_MSG_MOD(extack, "VLANs not supported on device");
                return -EOPNOTSUPP;
        }
 
-       if (vlan_find_dev(real_dev, protocol, vlan_id) != NULL)
+       if (vlan_find_dev(real_dev, protocol, vlan_id) != NULL) {
+               NL_SET_ERR_MSG_MOD(extack, "VLAN device already exists");
                return -EEXIST;
+       }
 
        return 0;
 }
@@ -215,7 +219,8 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
        if (vlan_id >= VLAN_VID_MASK)
                return -ERANGE;
 
-       err = vlan_check_real_dev(real_dev, htons(ETH_P_8021Q), vlan_id);
+       err = vlan_check_real_dev(real_dev, htons(ETH_P_8021Q), vlan_id,
+                                 NULL);
        if (err < 0)
                return err;
 
index e23aac3..44df1c3 100644 (file)
@@ -109,7 +109,8 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flag, u32 mask);
 void vlan_dev_get_realdev_name(const struct net_device *dev, char *result);
 
 int vlan_check_real_dev(struct net_device *real_dev,
-                       __be16 protocol, u16 vlan_id);
+                       __be16 protocol, u16 vlan_id,
+                       struct netlink_ext_ack *extack);
 void vlan_setup(struct net_device *dev);
 int register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack);
 void unregister_vlan_dev(struct net_device *dev, struct list_head *head);
index 6689c0b..9b60c1e 100644 (file)
@@ -47,14 +47,20 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[],
        int err;
 
        if (tb[IFLA_ADDRESS]) {
-               if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+               if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) {
+                       NL_SET_ERR_MSG_MOD(extack, "Invalid link address");
                        return -EINVAL;
-               if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+               }
+               if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) {
+                       NL_SET_ERR_MSG_MOD(extack, "Invalid link address");
                        return -EADDRNOTAVAIL;
+               }
        }
 
-       if (!data)
+       if (!data) {
+               NL_SET_ERR_MSG_MOD(extack, "VLAN properties not specified");
                return -EINVAL;
+       }
 
        if (data[IFLA_VLAN_PROTOCOL]) {
                switch (nla_get_be16(data[IFLA_VLAN_PROTOCOL])) {
@@ -62,29 +68,38 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[],
                case htons(ETH_P_8021AD):
                        break;
                default:
+                       NL_SET_ERR_MSG_MOD(extack, "Invalid VLAN protocol");
                        return -EPROTONOSUPPORT;
                }
        }
 
        if (data[IFLA_VLAN_ID]) {
                id = nla_get_u16(data[IFLA_VLAN_ID]);
-               if (id >= VLAN_VID_MASK)
+               if (id >= VLAN_VID_MASK) {
+                       NL_SET_ERR_MSG_MOD(extack, "Invalid VLAN id");
                        return -ERANGE;
+               }
        }
        if (data[IFLA_VLAN_FLAGS]) {
                flags = nla_data(data[IFLA_VLAN_FLAGS]);
                if ((flags->flags & flags->mask) &
                    ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP |
-                     VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP))
+                     VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP)) {
+                       NL_SET_ERR_MSG_MOD(extack, "Invalid VLAN flags");
                        return -EINVAL;
+               }
        }
 
        err = vlan_validate_qos_map(data[IFLA_VLAN_INGRESS_QOS]);
-       if (err < 0)
+       if (err < 0) {
+               NL_SET_ERR_MSG_MOD(extack, "Invalid ingress QOS map");
                return err;
+       }
        err = vlan_validate_qos_map(data[IFLA_VLAN_EGRESS_QOS]);
-       if (err < 0)
+       if (err < 0) {
+               NL_SET_ERR_MSG_MOD(extack, "Invalid egress QOS map");
                return err;
+       }
        return 0;
 }
 
@@ -126,14 +141,21 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
        __be16 proto;
        int err;
 
-       if (!data[IFLA_VLAN_ID])
+       if (!data[IFLA_VLAN_ID]) {
+               NL_SET_ERR_MSG_MOD(extack, "VLAN id not specified");
                return -EINVAL;
+       }
 
-       if (!tb[IFLA_LINK])
+       if (!tb[IFLA_LINK]) {
+               NL_SET_ERR_MSG_MOD(extack, "link not specified");
                return -EINVAL;
+       }
+
        real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
-       if (!real_dev)
+       if (!real_dev) {
+               NL_SET_ERR_MSG_MOD(extack, "link does not exist");
                return -ENODEV;
+       }
 
        if (data[IFLA_VLAN_PROTOCOL])
                proto = nla_get_be16(data[IFLA_VLAN_PROTOCOL]);
@@ -146,7 +168,8 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
        dev->priv_flags |= (real_dev->priv_flags & IFF_XMIT_DST_RELEASE);
        vlan->flags      = VLAN_FLAG_REORDER_HDR;
 
-       err = vlan_check_real_dev(real_dev, vlan->vlan_proto, vlan->vlan_id);
+       err = vlan_check_real_dev(real_dev, vlan->vlan_proto, vlan->vlan_id,
+                                 extack);
        if (err < 0)
                return err;
 
index df8d45e..ba554ce 100644 (file)
@@ -202,6 +202,8 @@ source "net/bridge/netfilter/Kconfig"
 
 endif
 
+source "net/bpfilter/Kconfig"
+
 source "net/dccp/Kconfig"
 source "net/sctp/Kconfig"
 source "net/rds/Kconfig"
index 77aadde..bdaf539 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_TLS)             += tls/
 obj-$(CONFIG_XFRM)             += xfrm/
 obj-$(CONFIG_UNIX)             += unix/
 obj-$(CONFIG_NET)              += ipv6/
+obj-$(CONFIG_BPFILTER)         += bpfilter/
 obj-$(CONFIG_PACKET)           += packet/
 obj-$(CONFIG_NET_KEY)          += key/
 obj-$(CONFIG_BRIDGE)           += bridge/
index 40d260f..b0ee9ed 100644 (file)
@@ -3422,6 +3422,37 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
        return 0;
 }
 
+int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
+                  const void *param)
+{
+       struct sk_buff *skb;
+
+       if (hci_opcode_ogf(opcode) != 0x3f) {
+               /* A controller receiving a command shall respond with either
+                * a Command Status Event or a Command Complete Event.
+                * Therefore, all standard HCI commands must be sent via the
+                * standard API, using hci_send_cmd or hci_cmd_sync helpers.
+                * Some vendors do not comply with this rule for vendor-specific
+                * commands and do not return any event. We want to support
+                * unresponded commands for such cases only.
+                */
+               bt_dev_err(hdev, "unresponded command not supported");
+               return -EINVAL;
+       }
+
+       skb = hci_prepare_cmd(hdev, opcode, plen, param);
+       if (!skb) {
+               bt_dev_err(hdev, "no memory for command (opcode 0x%4.4x)",
+                          opcode);
+               return -ENOMEM;
+       }
+
+       hci_send_frame(hdev, skb);
+
+       return 0;
+}
+EXPORT_SYMBOL(__hci_cmd_send);
+
 /* Get data from the previously sent command */
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
 {
index 139707c..235b5aa 100644 (file)
@@ -4942,10 +4942,14 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
                struct hci_ev_le_advertising_info *ev = ptr;
                s8 rssi;
 
-               rssi = ev->data[ev->length];
-               process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
-                                  ev->bdaddr_type, NULL, 0, rssi,
-                                  ev->data, ev->length);
+               if (ev->length <= HCI_MAX_AD_LENGTH) {
+                       rssi = ev->data[ev->length];
+                       process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
+                                          ev->bdaddr_type, NULL, 0, rssi,
+                                          ev->data, ev->length);
+               } else {
+                       bt_dev_err(hdev, "Dropping invalid advertising data");
+               }
 
                ptr += sizeof(*ev) + ev->length + 1;
        }
index 66c0781..e44d347 100644 (file)
@@ -122,7 +122,6 @@ void hci_req_sync_cancel(struct hci_dev *hdev, int err)
 struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
                                  const void *param, u8 event, u32 timeout)
 {
-       DECLARE_WAITQUEUE(wait, current);
        struct hci_request req;
        struct sk_buff *skb;
        int err = 0;
@@ -135,21 +134,14 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
 
        hdev->req_status = HCI_REQ_PEND;
 
-       add_wait_queue(&hdev->req_wait_q, &wait);
-       set_current_state(TASK_INTERRUPTIBLE);
-
        err = hci_req_run_skb(&req, hci_req_sync_complete);
-       if (err < 0) {
-               remove_wait_queue(&hdev->req_wait_q, &wait);
-               set_current_state(TASK_RUNNING);
+       if (err < 0)
                return ERR_PTR(err);
-       }
 
-       schedule_timeout(timeout);
+       err = wait_event_interruptible_timeout(hdev->req_wait_q,
+                       hdev->req_status != HCI_REQ_PEND, timeout);
 
-       remove_wait_queue(&hdev->req_wait_q, &wait);
-
-       if (signal_pending(current))
+       if (err == -ERESTARTSYS)
                return ERR_PTR(-EINTR);
 
        switch (hdev->req_status) {
@@ -197,7 +189,6 @@ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
                   unsigned long opt, u32 timeout, u8 *hci_status)
 {
        struct hci_request req;
-       DECLARE_WAITQUEUE(wait, current);
        int err = 0;
 
        BT_DBG("%s start", hdev->name);
@@ -213,16 +204,10 @@ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
                return err;
        }
 
-       add_wait_queue(&hdev->req_wait_q, &wait);
-       set_current_state(TASK_INTERRUPTIBLE);
-
        err = hci_req_run_skb(&req, hci_req_sync_complete);
        if (err < 0) {
                hdev->req_status = 0;
 
-               remove_wait_queue(&hdev->req_wait_q, &wait);
-               set_current_state(TASK_RUNNING);
-
                /* ENODATA means the HCI request command queue is empty.
                 * This can happen when a request with conditionals doesn't
                 * trigger any commands to be sent. This is normal behavior
@@ -240,11 +225,10 @@ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
                return err;
        }
 
-       schedule_timeout(timeout);
-
-       remove_wait_queue(&hdev->req_wait_q, &wait);
+       err = wait_event_interruptible_timeout(hdev->req_wait_q,
+                       hdev->req_status != HCI_REQ_PEND, timeout);
 
-       if (signal_pending(current))
+       if (err == -ERESTARTSYS)
                return -EINTR;
 
        switch (hdev->req_status) {
diff --git a/net/bpfilter/Kconfig b/net/bpfilter/Kconfig
new file mode 100644 (file)
index 0000000..60725c5
--- /dev/null
@@ -0,0 +1,16 @@
+menuconfig BPFILTER
+       bool "BPF based packet filtering framework (BPFILTER)"
+       default n
+       depends on NET && BPF
+       help
+         This builds experimental bpfilter framework that is aiming to
+         provide netfilter compatible functionality via BPF
+
+if BPFILTER
+config BPFILTER_UMH
+       tristate "bpfilter kernel module with user mode helper"
+       default m
+       help
+         This builds bpfilter kernel module with embedded user mode helper
+endif
+
diff --git a/net/bpfilter/Makefile b/net/bpfilter/Makefile
new file mode 100644 (file)
index 0000000..2af752c
--- /dev/null
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Linux BPFILTER layer.
+#
+
+hostprogs-y := bpfilter_umh
+bpfilter_umh-objs := main.o
+HOSTCFLAGS += -I. -Itools/include/
+ifeq ($(CONFIG_BPFILTER_UMH), y)
+# builtin bpfilter_umh should be compiled with -static
+# since rootfs isn't mounted at the time of __init
+# function is called and do_execv won't find elf interpreter
+HOSTLDFLAGS += -static
+endif
+
+# a bit of elf magic to convert bpfilter_umh binary into a binary blob
+# inside bpfilter_umh.o elf file referenced by
+# _binary_net_bpfilter_bpfilter_umh_start symbol
+# which bpfilter_kern.c passes further into umh blob loader at run-time
+quiet_cmd_copy_umh = GEN $@
+      cmd_copy_umh = echo ':' > $(obj)/.bpfilter_umh.o.cmd; \
+      $(OBJCOPY) -I binary -O $(CONFIG_OUTPUT_FORMAT) \
+      -B `$(OBJDUMP) -f $<|grep architecture|cut -d, -f1|cut -d' ' -f2` \
+      --rename-section .data=.init.rodata $< $@
+
+$(obj)/bpfilter_umh.o: $(obj)/bpfilter_umh
+       $(call cmd,copy_umh)
+
+obj-$(CONFIG_BPFILTER_UMH) += bpfilter.o
+bpfilter-objs += bpfilter_kern.o bpfilter_umh.o
diff --git a/net/bpfilter/bpfilter_kern.c b/net/bpfilter/bpfilter_kern.c
new file mode 100644 (file)
index 0000000..7596314
--- /dev/null
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/umh.h>
+#include <linux/bpfilter.h>
+#include <linux/sched.h>
+#include <linux/sched/signal.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include "msgfmt.h"
+
+#define UMH_start _binary_net_bpfilter_bpfilter_umh_start
+#define UMH_end _binary_net_bpfilter_bpfilter_umh_end
+
+extern char UMH_start;
+extern char UMH_end;
+
+static struct umh_info info;
+/* since ip_getsockopt() can run in parallel, serialize access to umh */
+static DEFINE_MUTEX(bpfilter_lock);
+
+static void shutdown_umh(struct umh_info *info)
+{
+       struct task_struct *tsk;
+
+       tsk = pid_task(find_vpid(info->pid), PIDTYPE_PID);
+       if (tsk)
+               force_sig(SIGKILL, tsk);
+       fput(info->pipe_to_umh);
+       fput(info->pipe_from_umh);
+}
+
+static void __stop_umh(void)
+{
+       if (bpfilter_process_sockopt) {
+               bpfilter_process_sockopt = NULL;
+               shutdown_umh(&info);
+       }
+}
+
+static void stop_umh(void)
+{
+       mutex_lock(&bpfilter_lock);
+       __stop_umh();
+       mutex_unlock(&bpfilter_lock);
+}
+
+static int __bpfilter_process_sockopt(struct sock *sk, int optname,
+                                     char __user *optval,
+                                     unsigned int optlen, bool is_set)
+{
+       struct mbox_request req;
+       struct mbox_reply reply;
+       loff_t pos;
+       ssize_t n;
+       int ret;
+
+       req.is_set = is_set;
+       req.pid = current->pid;
+       req.cmd = optname;
+       req.addr = (long)optval;
+       req.len = optlen;
+       mutex_lock(&bpfilter_lock);
+       n = __kernel_write(info.pipe_to_umh, &req, sizeof(req), &pos);
+       if (n != sizeof(req)) {
+               pr_err("write fail %zd\n", n);
+               __stop_umh();
+               ret = -EFAULT;
+               goto out;
+       }
+       pos = 0;
+       n = kernel_read(info.pipe_from_umh, &reply, sizeof(reply), &pos);
+       if (n != sizeof(reply)) {
+               pr_err("read fail %zd\n", n);
+               __stop_umh();
+               ret = -EFAULT;
+               goto out;
+       }
+       ret = reply.status;
+out:
+       mutex_unlock(&bpfilter_lock);
+       return ret;
+}
+
+static int __init load_umh(void)
+{
+       int err;
+
+       /* fork usermode process */
+       err = fork_usermode_blob(&UMH_start, &UMH_end - &UMH_start, &info);
+       if (err)
+               return err;
+       pr_info("Loaded bpfilter_umh pid %d\n", info.pid);
+
+       /* health check that usermode process started correctly */
+       if (__bpfilter_process_sockopt(NULL, 0, 0, 0, 0) != 0) {
+               stop_umh();
+               return -EFAULT;
+       }
+       bpfilter_process_sockopt = &__bpfilter_process_sockopt;
+       return 0;
+}
+
+static void __exit fini_umh(void)
+{
+       stop_umh();
+}
+module_init(load_umh);
+module_exit(fini_umh);
+MODULE_LICENSE("GPL");
diff --git a/net/bpfilter/main.c b/net/bpfilter/main.c
new file mode 100644 (file)
index 0000000..81bbc16
--- /dev/null
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <sys/uio.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "include/uapi/linux/bpf.h"
+#include <asm/unistd.h>
+#include "msgfmt.h"
+
+int debug_fd;
+
+static int handle_get_cmd(struct mbox_request *cmd)
+{
+       switch (cmd->cmd) {
+       case 0:
+               return 0;
+       default:
+               break;
+       }
+       return -ENOPROTOOPT;
+}
+
+static int handle_set_cmd(struct mbox_request *cmd)
+{
+       return -ENOPROTOOPT;
+}
+
+static void loop(void)
+{
+       while (1) {
+               struct mbox_request req;
+               struct mbox_reply reply;
+               int n;
+
+               n = read(0, &req, sizeof(req));
+               if (n != sizeof(req)) {
+                       dprintf(debug_fd, "invalid request %d\n", n);
+                       return;
+               }
+
+               reply.status = req.is_set ?
+                       handle_set_cmd(&req) :
+                       handle_get_cmd(&req);
+
+               n = write(1, &reply, sizeof(reply));
+               if (n != sizeof(reply)) {
+                       dprintf(debug_fd, "reply failed %d\n", n);
+                       return;
+               }
+       }
+}
+
+int main(void)
+{
+       debug_fd = open("/dev/console", 00000002 | 00000100);
+       dprintf(debug_fd, "Started bpfilter\n");
+       loop();
+       close(debug_fd);
+       return 0;
+}
diff --git a/net/bpfilter/msgfmt.h b/net/bpfilter/msgfmt.h
new file mode 100644 (file)
index 0000000..98d121c
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _NET_BPFILTER_MSGFMT_H
+#define _NET_BPFILTER_MSGFMT_H
+
+struct mbox_request {
+       __u64 addr;
+       __u32 len;
+       __u32 is_set;
+       __u32 cmd;
+       __u32 pid;
+};
+
+struct mbox_reply {
+       __u32 status;
+};
+
+#endif
index 47ba98d..46c1fe7 100644 (file)
@@ -161,8 +161,8 @@ static int ebt_stp_mt_check(const struct xt_mtchk_param *par)
        /* Make sure the match only receives stp frames */
        if (!par->nft_compat &&
            (!ether_addr_equal(e->destmac, eth_stp_addr) ||
-            !is_broadcast_ether_addr(e->destmsk) ||
-            !(e->bitmask & EBT_DESTMAC)))
+            !(e->bitmask & EBT_DESTMAC) ||
+            !is_broadcast_ether_addr(e->destmsk)))
                return -EINVAL;
 
        return 0;
index 9f43901..1844d9b 100644 (file)
@@ -2125,7 +2125,7 @@ static bool remove_xps_queue_cpu(struct net_device *dev,
                int i, j;
 
                for (i = count, j = offset; i--; j++) {
-                       if (!remove_xps_queue(dev_maps, cpu, j))
+                       if (!remove_xps_queue(dev_maps, tci, j))
                                break;
                }
 
@@ -3244,7 +3244,7 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
                        rc = NET_XMIT_DROP;
                } else {
                        rc = q->enqueue(skb, q, &to_free) & NET_XMIT_MASK;
-                       __qdisc_run(q);
+                       qdisc_run(q);
                }
 
                if (unlikely(to_free))
index ad13173..475246b 100644 (file)
@@ -453,6 +453,27 @@ static void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
                                msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 }
 
+static int devlink_nl_port_attrs_put(struct sk_buff *msg,
+                                    struct devlink_port *devlink_port)
+{
+       struct devlink_port_attrs *attrs = &devlink_port->attrs;
+
+       if (!attrs->set)
+               return 0;
+       if (nla_put_u16(msg, DEVLINK_ATTR_PORT_FLAVOUR, attrs->flavour))
+               return -EMSGSIZE;
+       if (nla_put_u32(msg, DEVLINK_ATTR_PORT_NUMBER, attrs->port_number))
+               return -EMSGSIZE;
+       if (!attrs->split)
+               return 0;
+       if (nla_put_u32(msg, DEVLINK_ATTR_PORT_SPLIT_GROUP, attrs->port_number))
+               return -EMSGSIZE;
+       if (nla_put_u32(msg, DEVLINK_ATTR_PORT_SPLIT_SUBPORT_NUMBER,
+                       attrs->split_subport_number))
+               return -EMSGSIZE;
+       return 0;
+}
+
 static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink,
                                struct devlink_port *devlink_port,
                                enum devlink_command cmd, u32 portid,
@@ -492,9 +513,7 @@ static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink,
                                   ibdev->name))
                        goto nla_put_failure;
        }
-       if (devlink_port->split &&
-           nla_put_u32(msg, DEVLINK_ATTR_PORT_SPLIT_GROUP,
-                       devlink_port->split_group))
+       if (devlink_nl_port_attrs_put(msg, devlink_port))
                goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
@@ -2737,7 +2756,8 @@ static const struct genl_ops devlink_nl_ops[] = {
                .doit = devlink_nl_cmd_eswitch_set_doit,
                .policy = devlink_nl_policy,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
+                                 DEVLINK_NL_FLAG_NO_LOCK,
        },
        {
                .cmd = DEVLINK_CMD_DPIPE_TABLE_GET,
@@ -2971,19 +2991,64 @@ void devlink_port_type_clear(struct devlink_port *devlink_port)
 EXPORT_SYMBOL_GPL(devlink_port_type_clear);
 
 /**
- *     devlink_port_split_set - Set port is split
+ *     devlink_port_attrs_set - Set port attributes
  *
  *     @devlink_port: devlink port
- *     @split_group: split group - identifies group split port is part of
+ *     @flavour: flavour of the port
+ *     @port_number: number of the port that is facing user, for example
+ *                   the front panel port number
+ *     @split: indicates if this is split port
+ *     @split_subport_number: if the port is split, this is the number
+ *                            of subport.
  */
-void devlink_port_split_set(struct devlink_port *devlink_port,
-                           u32 split_group)
-{
-       devlink_port->split = true;
-       devlink_port->split_group = split_group;
+void devlink_port_attrs_set(struct devlink_port *devlink_port,
+                           enum devlink_port_flavour flavour,
+                           u32 port_number, bool split,
+                           u32 split_subport_number)
+{
+       struct devlink_port_attrs *attrs = &devlink_port->attrs;
+
+       attrs->set = true;
+       attrs->flavour = flavour;
+       attrs->port_number = port_number;
+       attrs->split = split;
+       attrs->split_subport_number = split_subport_number;
        devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
 }
-EXPORT_SYMBOL_GPL(devlink_port_split_set);
+EXPORT_SYMBOL_GPL(devlink_port_attrs_set);
+
+int devlink_port_get_phys_port_name(struct devlink_port *devlink_port,
+                                   char *name, size_t len)
+{
+       struct devlink_port_attrs *attrs = &devlink_port->attrs;
+       int n = 0;
+
+       if (!attrs->set)
+               return -EOPNOTSUPP;
+
+       switch (attrs->flavour) {
+       case DEVLINK_PORT_FLAVOUR_PHYSICAL:
+               if (!attrs->split)
+                       n = snprintf(name, len, "p%u", attrs->port_number);
+               else
+                       n = snprintf(name, len, "p%us%u", attrs->port_number,
+                                    attrs->split_subport_number);
+               break;
+       case DEVLINK_PORT_FLAVOUR_CPU:
+       case DEVLINK_PORT_FLAVOUR_DSA:
+               /* As CPU and DSA ports do not have a netdevice associated
+                * case should not ever happen.
+                */
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       if (n >= len)
+               return -EINVAL;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_port_get_phys_port_name);
 
 int devlink_sb_register(struct devlink *devlink, unsigned int sb_index,
                        u32 size, u16 ingress_pools_count,
index 6877426..51ea7dd 100644 (file)
 #include <net/xfrm.h>
 #include <linux/bpf_trace.h>
 #include <net/xdp_sock.h>
+#include <linux/inetdevice.h>
+#include <net/ip_fib.h>
+#include <net/flow.h>
+#include <net/arp.h>
 
 /**
  *     sk_filter_trim_cap - run a packet through a socket filter
@@ -645,11 +649,18 @@ do_pass:
 
 #define BPF_EMIT_JMP                                                   \
        do {                                                            \
+               const s32 off_min = S16_MIN, off_max = S16_MAX;         \
+               s32 off;                                                \
+                                                                       \
                if (target >= len || target < 0)                        \
                        goto err;                                       \
-               insn->off = addrs ? addrs[target] - addrs[i] - 1 : 0;   \
+               off = addrs ? addrs[target] - addrs[i] - 1 : 0;         \
                /* Adjust pc relative offset for 2nd or 3rd insn. */    \
-               insn->off -= insn - tmp_insns;                          \
+               off -= insn - tmp_insns;                                \
+               /* Reject anything not fitting into insn->off. */       \
+               if (off < off_min || off > off_max)                     \
+                       goto err;                                       \
+               insn->off = off;                                        \
        } while (0)
 
                case BPF_JMP | BPF_JA:
@@ -2070,6 +2081,33 @@ static const struct bpf_func_proto bpf_redirect_proto = {
        .arg2_type      = ARG_ANYTHING,
 };
 
+BPF_CALL_4(bpf_sk_redirect_hash, struct sk_buff *, skb,
+          struct bpf_map *, map, void *, key, u64, flags)
+{
+       struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
+
+       /* If user passes invalid input drop the packet. */
+       if (unlikely(flags & ~(BPF_F_INGRESS)))
+               return SK_DROP;
+
+       tcb->bpf.flags = flags;
+       tcb->bpf.sk_redir = __sock_hash_lookup_elem(map, key);
+       if (!tcb->bpf.sk_redir)
+               return SK_DROP;
+
+       return SK_PASS;
+}
+
+static const struct bpf_func_proto bpf_sk_redirect_hash_proto = {
+       .func           = bpf_sk_redirect_hash,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_CONST_MAP_PTR,
+       .arg3_type      = ARG_PTR_TO_MAP_KEY,
+       .arg4_type      = ARG_ANYTHING,
+};
+
 BPF_CALL_4(bpf_sk_redirect_map, struct sk_buff *, skb,
           struct bpf_map *, map, u32, key, u64, flags)
 {
@@ -2079,9 +2117,10 @@ BPF_CALL_4(bpf_sk_redirect_map, struct sk_buff *, skb,
        if (unlikely(flags & ~(BPF_F_INGRESS)))
                return SK_DROP;
 
-       tcb->bpf.key = key;
        tcb->bpf.flags = flags;
-       tcb->bpf.map = map;
+       tcb->bpf.sk_redir = __sock_map_lookup_elem(map, key);
+       if (!tcb->bpf.sk_redir)
+               return SK_DROP;
 
        return SK_PASS;
 }
@@ -2089,16 +2128,8 @@ BPF_CALL_4(bpf_sk_redirect_map, struct sk_buff *, skb,
 struct sock *do_sk_redirect_map(struct sk_buff *skb)
 {
        struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
-       struct sock *sk = NULL;
-
-       if (tcb->bpf.map) {
-               sk = __sock_map_lookup_elem(tcb->bpf.map, tcb->bpf.key);
-
-               tcb->bpf.key = 0;
-               tcb->bpf.map = NULL;
-       }
 
-       return sk;
+       return tcb->bpf.sk_redir;
 }
 
 static const struct bpf_func_proto bpf_sk_redirect_map_proto = {
@@ -2111,32 +2142,49 @@ static const struct bpf_func_proto bpf_sk_redirect_map_proto = {
        .arg4_type      = ARG_ANYTHING,
 };
 
-BPF_CALL_4(bpf_msg_redirect_map, struct sk_msg_buff *, msg,
-          struct bpf_map *, map, u32, key, u64, flags)
+BPF_CALL_4(bpf_msg_redirect_hash, struct sk_msg_buff *, msg,
+          struct bpf_map *, map, void *, key, u64, flags)
 {
        /* If user passes invalid input drop the packet. */
        if (unlikely(flags & ~(BPF_F_INGRESS)))
                return SK_DROP;
 
-       msg->key = key;
        msg->flags = flags;
-       msg->map = map;
+       msg->sk_redir = __sock_hash_lookup_elem(map, key);
+       if (!msg->sk_redir)
+               return SK_DROP;
 
        return SK_PASS;
 }
 
-struct sock *do_msg_redirect_map(struct sk_msg_buff *msg)
+static const struct bpf_func_proto bpf_msg_redirect_hash_proto = {
+       .func           = bpf_msg_redirect_hash,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_CONST_MAP_PTR,
+       .arg3_type      = ARG_PTR_TO_MAP_KEY,
+       .arg4_type      = ARG_ANYTHING,
+};
+
+BPF_CALL_4(bpf_msg_redirect_map, struct sk_msg_buff *, msg,
+          struct bpf_map *, map, u32, key, u64, flags)
 {
-       struct sock *sk = NULL;
+       /* If user passes invalid input drop the packet. */
+       if (unlikely(flags & ~(BPF_F_INGRESS)))
+               return SK_DROP;
 
-       if (msg->map) {
-               sk = __sock_map_lookup_elem(msg->map, msg->key);
+       msg->flags = flags;
+       msg->sk_redir = __sock_map_lookup_elem(map, key);
+       if (!msg->sk_redir)
+               return SK_DROP;
 
-               msg->key = 0;
-               msg->map = NULL;
-       }
+       return SK_PASS;
+}
 
-       return sk;
+struct sock *do_msg_redirect_map(struct sk_msg_buff *msg)
+{
+       return msg->sk_redir;
 }
 
 static const struct bpf_func_proto bpf_msg_redirect_map_proto = {
@@ -4032,6 +4080,265 @@ static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = {
 };
 #endif
 
+#if IS_ENABLED(CONFIG_INET) || IS_ENABLED(CONFIG_IPV6)
+static int bpf_fib_set_fwd_params(struct bpf_fib_lookup *params,
+                                 const struct neighbour *neigh,
+                                 const struct net_device *dev)
+{
+       memcpy(params->dmac, neigh->ha, ETH_ALEN);
+       memcpy(params->smac, dev->dev_addr, ETH_ALEN);
+       params->h_vlan_TCI = 0;
+       params->h_vlan_proto = 0;
+
+       return dev->ifindex;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_INET)
+static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
+                              u32 flags)
+{
+       struct in_device *in_dev;
+       struct neighbour *neigh;
+       struct net_device *dev;
+       struct fib_result res;
+       struct fib_nh *nh;
+       struct flowi4 fl4;
+       int err;
+
+       dev = dev_get_by_index_rcu(net, params->ifindex);
+       if (unlikely(!dev))
+               return -ENODEV;
+
+       /* verify forwarding is enabled on this interface */
+       in_dev = __in_dev_get_rcu(dev);
+       if (unlikely(!in_dev || !IN_DEV_FORWARD(in_dev)))
+               return 0;
+
+       if (flags & BPF_FIB_LOOKUP_OUTPUT) {
+               fl4.flowi4_iif = 1;
+               fl4.flowi4_oif = params->ifindex;
+       } else {
+               fl4.flowi4_iif = params->ifindex;
+               fl4.flowi4_oif = 0;
+       }
+       fl4.flowi4_tos = params->tos & IPTOS_RT_MASK;
+       fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
+       fl4.flowi4_flags = 0;
+
+       fl4.flowi4_proto = params->l4_protocol;
+       fl4.daddr = params->ipv4_dst;
+       fl4.saddr = params->ipv4_src;
+       fl4.fl4_sport = params->sport;
+       fl4.fl4_dport = params->dport;
+
+       if (flags & BPF_FIB_LOOKUP_DIRECT) {
+               u32 tbid = l3mdev_fib_table_rcu(dev) ? : RT_TABLE_MAIN;
+               struct fib_table *tb;
+
+               tb = fib_get_table(net, tbid);
+               if (unlikely(!tb))
+                       return 0;
+
+               err = fib_table_lookup(tb, &fl4, &res, FIB_LOOKUP_NOREF);
+       } else {
+               fl4.flowi4_mark = 0;
+               fl4.flowi4_secid = 0;
+               fl4.flowi4_tun_key.tun_id = 0;
+               fl4.flowi4_uid = sock_net_uid(net, NULL);
+
+               err = fib_lookup(net, &fl4, &res, FIB_LOOKUP_NOREF);
+       }
+
+       if (err || res.type != RTN_UNICAST)
+               return 0;
+
+       if (res.fi->fib_nhs > 1)
+               fib_select_path(net, &res, &fl4, NULL);
+
+       nh = &res.fi->fib_nh[res.nh_sel];
+
+       /* do not handle lwt encaps right now */
+       if (nh->nh_lwtstate)
+               return 0;
+
+       dev = nh->nh_dev;
+       if (unlikely(!dev))
+               return 0;
+
+       if (nh->nh_gw)
+               params->ipv4_dst = nh->nh_gw;
+
+       params->rt_metric = res.fi->fib_priority;
+
+       /* xdp and cls_bpf programs are run in RCU-bh so
+        * rcu_read_lock_bh is not needed here
+        */
+       neigh = __ipv4_neigh_lookup_noref(dev, (__force u32)params->ipv4_dst);
+       if (neigh)
+               return bpf_fib_set_fwd_params(params, neigh, dev);
+
+       return 0;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6)
+static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
+                              u32 flags)
+{
+       struct in6_addr *src = (struct in6_addr *) params->ipv6_src;
+       struct in6_addr *dst = (struct in6_addr *) params->ipv6_dst;
+       struct neighbour *neigh;
+       struct net_device *dev;
+       struct inet6_dev *idev;
+       struct fib6_info *f6i;
+       struct flowi6 fl6;
+       int strict = 0;
+       int oif;
+
+       /* link local addresses are never forwarded */
+       if (rt6_need_strict(dst) || rt6_need_strict(src))
+               return 0;
+
+       dev = dev_get_by_index_rcu(net, params->ifindex);
+       if (unlikely(!dev))
+               return -ENODEV;
+
+       idev = __in6_dev_get_safely(dev);
+       if (unlikely(!idev || !net->ipv6.devconf_all->forwarding))
+               return 0;
+
+       if (flags & BPF_FIB_LOOKUP_OUTPUT) {
+               fl6.flowi6_iif = 1;
+               oif = fl6.flowi6_oif = params->ifindex;
+       } else {
+               oif = fl6.flowi6_iif = params->ifindex;
+               fl6.flowi6_oif = 0;
+               strict = RT6_LOOKUP_F_HAS_SADDR;
+       }
+       fl6.flowlabel = params->flowlabel;
+       fl6.flowi6_scope = 0;
+       fl6.flowi6_flags = 0;
+       fl6.mp_hash = 0;
+
+       fl6.flowi6_proto = params->l4_protocol;
+       fl6.daddr = *dst;
+       fl6.saddr = *src;
+       fl6.fl6_sport = params->sport;
+       fl6.fl6_dport = params->dport;
+
+       if (flags & BPF_FIB_LOOKUP_DIRECT) {
+               u32 tbid = l3mdev_fib_table_rcu(dev) ? : RT_TABLE_MAIN;
+               struct fib6_table *tb;
+
+               tb = ipv6_stub->fib6_get_table(net, tbid);
+               if (unlikely(!tb))
+                       return 0;
+
+               f6i = ipv6_stub->fib6_table_lookup(net, tb, oif, &fl6, strict);
+       } else {
+               fl6.flowi6_mark = 0;
+               fl6.flowi6_secid = 0;
+               fl6.flowi6_tun_key.tun_id = 0;
+               fl6.flowi6_uid = sock_net_uid(net, NULL);
+
+               f6i = ipv6_stub->fib6_lookup(net, oif, &fl6, strict);
+       }
+
+       if (unlikely(IS_ERR_OR_NULL(f6i) || f6i == net->ipv6.fib6_null_entry))
+               return 0;
+
+       if (unlikely(f6i->fib6_flags & RTF_REJECT ||
+           f6i->fib6_type != RTN_UNICAST))
+               return 0;
+
+       if (f6i->fib6_nsiblings && fl6.flowi6_oif == 0)
+               f6i = ipv6_stub->fib6_multipath_select(net, f6i, &fl6,
+                                                      fl6.flowi6_oif, NULL,
+                                                      strict);
+
+       if (f6i->fib6_nh.nh_lwtstate)
+               return 0;
+
+       if (f6i->fib6_flags & RTF_GATEWAY)
+               *dst = f6i->fib6_nh.nh_gw;
+
+       dev = f6i->fib6_nh.nh_dev;
+       params->rt_metric = f6i->fib6_metric;
+
+       /* xdp and cls_bpf programs are run in RCU-bh so rcu_read_lock_bh is
+        * not needed here. Can not use __ipv6_neigh_lookup_noref here
+        * because we need to get nd_tbl via the stub
+        */
+       neigh = ___neigh_lookup_noref(ipv6_stub->nd_tbl, neigh_key_eq128,
+                                     ndisc_hashfn, dst, dev);
+       if (neigh)
+               return bpf_fib_set_fwd_params(params, neigh, dev);
+
+       return 0;
+}
+#endif
+
+BPF_CALL_4(bpf_xdp_fib_lookup, struct xdp_buff *, ctx,
+          struct bpf_fib_lookup *, params, int, plen, u32, flags)
+{
+       if (plen < sizeof(*params))
+               return -EINVAL;
+
+       switch (params->family) {
+#if IS_ENABLED(CONFIG_INET)
+       case AF_INET:
+               return bpf_ipv4_fib_lookup(dev_net(ctx->rxq->dev), params,
+                                          flags);
+#endif
+#if IS_ENABLED(CONFIG_IPV6)
+       case AF_INET6:
+               return bpf_ipv6_fib_lookup(dev_net(ctx->rxq->dev), params,
+                                          flags);
+#endif
+       }
+       return 0;
+}
+
+static const struct bpf_func_proto bpf_xdp_fib_lookup_proto = {
+       .func           = bpf_xdp_fib_lookup,
+       .gpl_only       = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_ANYTHING,
+};
+
+BPF_CALL_4(bpf_skb_fib_lookup, struct sk_buff *, skb,
+          struct bpf_fib_lookup *, params, int, plen, u32, flags)
+{
+       if (plen < sizeof(*params))
+               return -EINVAL;
+
+       switch (params->family) {
+#if IS_ENABLED(CONFIG_INET)
+       case AF_INET:
+               return bpf_ipv4_fib_lookup(dev_net(skb->dev), params, flags);
+#endif
+#if IS_ENABLED(CONFIG_IPV6)
+       case AF_INET6:
+               return bpf_ipv6_fib_lookup(dev_net(skb->dev), params, flags);
+#endif
+       }
+       return -ENOTSUPP;
+}
+
+static const struct bpf_func_proto bpf_skb_fib_lookup_proto = {
+       .func           = bpf_skb_fib_lookup,
+       .gpl_only       = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_MEM,
+       .arg3_type      = ARG_CONST_SIZE,
+       .arg4_type      = ARG_ANYTHING,
+};
+
 static const struct bpf_func_proto *
 bpf_base_func_proto(enum bpf_func_id func_id)
 {
@@ -4181,6 +4488,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
        case BPF_FUNC_skb_get_xfrm_state:
                return &bpf_skb_get_xfrm_state_proto;
 #endif
+       case BPF_FUNC_fib_lookup:
+               return &bpf_skb_fib_lookup_proto;
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -4206,6 +4515,8 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_xdp_redirect_map_proto;
        case BPF_FUNC_xdp_adjust_tail:
                return &bpf_xdp_adjust_tail_proto;
+       case BPF_FUNC_fib_lookup:
+               return &bpf_xdp_fib_lookup_proto;
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -4250,6 +4561,8 @@ sock_ops_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_sock_ops_cb_flags_set_proto;
        case BPF_FUNC_sock_map_update:
                return &bpf_sock_map_update_proto;
+       case BPF_FUNC_sock_hash_update:
+               return &bpf_sock_hash_update_proto;
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -4261,6 +4574,8 @@ sk_msg_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
        switch (func_id) {
        case BPF_FUNC_msg_redirect_map:
                return &bpf_msg_redirect_map_proto;
+       case BPF_FUNC_msg_redirect_hash:
+               return &bpf_msg_redirect_hash_proto;
        case BPF_FUNC_msg_apply_bytes:
                return &bpf_msg_apply_bytes_proto;
        case BPF_FUNC_msg_cork_bytes:
@@ -4292,6 +4607,8 @@ sk_skb_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_get_socket_uid_proto;
        case BPF_FUNC_sk_redirect_map:
                return &bpf_sk_redirect_map_proto;
+       case BPF_FUNC_sk_redirect_hash:
+               return &bpf_sk_redirect_hash_proto;
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -4645,8 +4962,15 @@ static bool xdp_is_valid_access(int off, int size,
                                const struct bpf_prog *prog,
                                struct bpf_insn_access_aux *info)
 {
-       if (type == BPF_WRITE)
+       if (type == BPF_WRITE) {
+               if (bpf_prog_is_dev_bound(prog->aux)) {
+                       switch (off) {
+                       case offsetof(struct xdp_md, rx_queue_index):
+                               return __is_valid_xdp_access(off, size);
+                       }
+               }
                return false;
+       }
 
        switch (off) {
        case offsetof(struct xdp_md, data):
index 042cfc6..435a0ba 100644 (file)
@@ -1613,7 +1613,7 @@ static void __sk_free(struct sock *sk)
        if (likely(sk->sk_net_refcnt))
                sock_inuse_add(sock_net(sk), -1);
 
-       if (unlikely(sock_diag_has_destroy_listeners(sk) && sk->sk_net_refcnt))
+       if (unlikely(sk->sk_net_refcnt && sock_diag_has_destroy_listeners(sk)))
                sock_diag_broadcast_destroy(sk);
        else
                sk_destruct(sk);
index adf50fb..dc5d9af 100644 (file)
@@ -258,11 +258,13 @@ static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst)
 static int dsa_port_setup(struct dsa_port *dp)
 {
        struct dsa_switch *ds = dp->ds;
-       int err;
+       int err = 0;
 
        memset(&dp->devlink_port, 0, sizeof(dp->devlink_port));
 
-       err = devlink_port_register(ds->devlink, &dp->devlink_port, dp->index);
+       if (dp->type != DSA_PORT_TYPE_UNUSED)
+               err = devlink_port_register(ds->devlink, &dp->devlink_port,
+                                           dp->index);
        if (err)
                return err;
 
@@ -270,7 +272,28 @@ static int dsa_port_setup(struct dsa_port *dp)
        case DSA_PORT_TYPE_UNUSED:
                break;
        case DSA_PORT_TYPE_CPU:
+               /* dp->index is used now as port_number. However
+                * CPU ports should have separate numbering
+                * independent from front panel port numbers.
+                */
+               devlink_port_attrs_set(&dp->devlink_port,
+                                      DEVLINK_PORT_FLAVOUR_CPU,
+                                      dp->index, false, 0);
+               err = dsa_port_link_register_of(dp);
+               if (err) {
+                       dev_err(ds->dev, "failed to setup link for port %d.%d\n",
+                               ds->index, dp->index);
+                       return err;
+               }
+               break;
        case DSA_PORT_TYPE_DSA:
+               /* dp->index is used now as port_number. However
+                * DSA ports should have separate numbering
+                * independent from front panel port numbers.
+                */
+               devlink_port_attrs_set(&dp->devlink_port,
+                                      DEVLINK_PORT_FLAVOUR_DSA,
+                                      dp->index, false, 0);
                err = dsa_port_link_register_of(dp);
                if (err) {
                        dev_err(ds->dev, "failed to setup link for port %d.%d\n",
@@ -279,6 +302,9 @@ static int dsa_port_setup(struct dsa_port *dp)
                }
                break;
        case DSA_PORT_TYPE_USER:
+               devlink_port_attrs_set(&dp->devlink_port,
+                                      DEVLINK_PORT_FLAVOUR_PHYSICAL,
+                                      dp->index, false, 0);
                err = dsa_slave_create(dp);
                if (err)
                        dev_err(ds->dev, "failed to create slave for port %d.%d\n",
@@ -293,7 +319,8 @@ static int dsa_port_setup(struct dsa_port *dp)
 
 static void dsa_port_teardown(struct dsa_port *dp)
 {
-       devlink_port_unregister(&dp->devlink_port);
+       if (dp->type != DSA_PORT_TYPE_UNUSED)
+               devlink_port_unregister(&dp->devlink_port);
 
        switch (dp->type) {
        case DSA_PORT_TYPE_UNUSED:
index b379520..eec9569 100644 (file)
@@ -14,7 +14,9 @@ obj-y     := route.o inetpeer.o protocol.o \
             udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \
             fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \
             inet_fragment.o ping.o ip_tunnel_core.o gre_offload.o \
-            metrics.o
+            metrics.o netlink.o
+
+obj-$(CONFIG_BPFILTER) += bpfilter/
 
 obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o
 obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o
diff --git a/net/ipv4/bpfilter/Makefile b/net/ipv4/bpfilter/Makefile
new file mode 100644 (file)
index 0000000..ce262d7
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_BPFILTER) += sockopt.o
+
diff --git a/net/ipv4/bpfilter/sockopt.c b/net/ipv4/bpfilter/sockopt.c
new file mode 100644 (file)
index 0000000..42a96d2
--- /dev/null
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/uaccess.h>
+#include <linux/bpfilter.h>
+#include <uapi/linux/bpf.h>
+#include <linux/wait.h>
+#include <linux/kmod.h>
+
+int (*bpfilter_process_sockopt)(struct sock *sk, int optname,
+                               char __user *optval,
+                               unsigned int optlen, bool is_set);
+EXPORT_SYMBOL_GPL(bpfilter_process_sockopt);
+
+int bpfilter_mbox_request(struct sock *sk, int optname, char __user *optval,
+                         unsigned int optlen, bool is_set)
+{
+       if (!bpfilter_process_sockopt) {
+               int err = request_module("bpfilter");
+
+               if (err)
+                       return err;
+               if (!bpfilter_process_sockopt)
+                       return -ECHILD;
+       }
+       return bpfilter_process_sockopt(sk, optname, optval, optlen, is_set);
+}
+
+int bpfilter_ip_set_sockopt(struct sock *sk, int optname, char __user *optval,
+                           unsigned int optlen)
+{
+       return bpfilter_mbox_request(sk, optname, optval, optlen, true);
+}
+
+int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char __user *optval,
+                           int __user *optlen)
+{
+       int len;
+
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       return bpfilter_mbox_request(sk, optname, optval, len, false);
+}
index f05afaf..897ae92 100644 (file)
@@ -326,10 +326,11 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
                                 u8 tos, int oif, struct net_device *dev,
                                 int rpf, struct in_device *idev, u32 *itag)
 {
+       struct net *net = dev_net(dev);
+       struct flow_keys flkeys;
        int ret, no_addr;
        struct fib_result res;
        struct flowi4 fl4;
-       struct net *net = dev_net(dev);
        bool dev_match;
 
        fl4.flowi4_oif = 0;
@@ -347,6 +348,11 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
        no_addr = idev->ifa_list == NULL;
 
        fl4.flowi4_mark = IN_DEV_SRC_VMARK(idev) ? skb->mark : 0;
+       if (!fib4_rules_early_flow_dissect(net, skb, &fl4, &flkeys)) {
+               fl4.flowi4_proto = 0;
+               fl4.fl4_sport = 0;
+               fl4.fl4_dport = 0;
+       }
 
        trace_fib_validate_source(dev, &fl4);
 
@@ -643,6 +649,9 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = {
        [RTA_ENCAP]             = { .type = NLA_NESTED },
        [RTA_UID]               = { .type = NLA_U32 },
        [RTA_MARK]              = { .type = NLA_U32 },
+       [RTA_IP_PROTO]          = { .type = NLA_U8 },
+       [RTA_SPORT]             = { .type = NLA_U16 },
+       [RTA_DPORT]             = { .type = NLA_U16 },
 };
 
 static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
index 2409e64..2d8efee 100644 (file)
@@ -734,10 +734,12 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
                erspan_build_header(skb, ntohl(tunnel->parms.o_key),
                                    tunnel->index,
                                    truncate, true);
-       else
+       else if (tunnel->erspan_ver == 2)
                erspan_build_header_v2(skb, ntohl(tunnel->parms.o_key),
                                       tunnel->dir, tunnel->hwid,
                                       truncate, true);
+       else
+               goto free_skb;
 
        tunnel->parms.o_flags &= ~TUNNEL_KEY;
        __gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_ERSPAN));
index b5e21eb..af5a830 100644 (file)
@@ -1053,7 +1053,8 @@ alloc_new_skb:
                if (copy > length)
                        copy = length;
 
-               if (!(rt->dst.dev->features&NETIF_F_SG)) {
+               if (!(rt->dst.dev->features&NETIF_F_SG) &&
+                   skb_tailroom(skb) >= copy) {
                        unsigned int off;
 
                        off = skb->len;
index 5ad2d8e..e0791fa 100644 (file)
@@ -47,6 +47,8 @@
 #include <linux/errqueue.h>
 #include <linux/uaccess.h>
 
+#include <linux/bpfilter.h>
+
 /*
  *     SOL_IP control messages.
  */
@@ -1244,6 +1246,11 @@ int ip_setsockopt(struct sock *sk, int level,
                return -ENOPROTOOPT;
 
        err = do_ip_setsockopt(sk, level, optname, optval, optlen);
+#ifdef CONFIG_BPFILTER
+       if (optname >= BPFILTER_IPT_SO_SET_REPLACE &&
+           optname < BPFILTER_IPT_SET_MAX)
+               err = bpfilter_ip_set_sockopt(sk, optname, optval, optlen);
+#endif
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default case */
        if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
@@ -1552,6 +1559,11 @@ int ip_getsockopt(struct sock *sk, int level,
        int err;
 
        err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0);
+#ifdef CONFIG_BPFILTER
+       if (optname >= BPFILTER_IPT_SO_GET_INFO &&
+           optname < BPFILTER_IPT_GET_MAX)
+               err = bpfilter_ip_get_sockopt(sk, optname, optval, optlen);
+#endif
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default case */
        if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
@@ -1584,6 +1596,11 @@ int compat_ip_getsockopt(struct sock *sk, int level, int optname,
        err = do_ip_getsockopt(sk, level, optname, optval, optlen,
                MSG_CMSG_COMPAT);
 
+#ifdef CONFIG_BPFILTER
+       if (optname >= BPFILTER_IPT_SO_GET_INFO &&
+           optname < BPFILTER_IPT_GET_MAX)
+               err = bpfilter_ip_get_sockopt(sk, optname, optval, optlen);
+#endif
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default case */
        if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
index ddcc56c..38ab97b 100644 (file)
@@ -34,6 +34,7 @@
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
 MODULE_DESCRIPTION("IPv4 packet filter");
+MODULE_ALIAS("ipt_icmp");
 
 void *ipt_alloc_initial_table(const struct xt_table *info)
 {
index fd01f13..12843c9 100644 (file)
@@ -89,10 +89,10 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
                        return true ^ invert;
        }
 
+       memset(&flow, 0, sizeof(flow));
        flow.flowi4_iif = LOOPBACK_IFINDEX;
        flow.daddr = iph->saddr;
        flow.saddr = rpfilter_get_saddr(iph->daddr);
-       flow.flowi4_oif = 0;
        flow.flowi4_mark = info->flags & XT_RPFILTER_VALID_MARK ? skb->mark : 0;
        flow.flowi4_tos = RT_TOS(iph->tos);
        flow.flowi4_scope = RT_SCOPE_UNIVERSE;
diff --git a/net/ipv4/netlink.c b/net/ipv4/netlink.c
new file mode 100644 (file)
index 0000000..f86bb4f
--- /dev/null
@@ -0,0 +1,23 @@
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/types.h>
+#include <net/net_namespace.h>
+#include <net/netlink.h>
+#include <net/ip.h>
+
+int rtm_getroute_parse_ip_proto(struct nlattr *attr, u8 *ip_proto,
+                               struct netlink_ext_ack *extack)
+{
+       *ip_proto = nla_get_u8(attr);
+
+       switch (*ip_proto) {
+       case IPPROTO_TCP:
+       case IPPROTO_UDP:
+       case IPPROTO_ICMP:
+               return 0;
+       default:
+               NL_SET_ERR_MSG(extack, "Unsupported ip proto");
+               return -EOPNOTSUPP;
+       }
+}
+EXPORT_SYMBOL_GPL(rtm_getroute_parse_ip_proto);
index 261b71d..6c1ff89 100644 (file)
@@ -298,6 +298,7 @@ static const struct snmp_mib snmp4_net_list[] = {
        SNMP_MIB_ITEM("TCPMTUPSuccess", LINUX_MIB_TCPMTUPSUCCESS),
        SNMP_MIB_ITEM("TCPDelivered", LINUX_MIB_TCPDELIVERED),
        SNMP_MIB_ITEM("TCPDeliveredCE", LINUX_MIB_TCPDELIVEREDCE),
+       SNMP_MIB_ITEM("TCPAckCompressed", LINUX_MIB_TCPACKCOMPRESSED),
        SNMP_MIB_SENTINEL
 };
 
index 29268ef..0e401dc 100644 (file)
@@ -1961,8 +1961,13 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
        fl4.saddr = saddr;
        fl4.flowi4_uid = sock_net_uid(net, NULL);
 
-       if (fib4_rules_early_flow_dissect(net, skb, &fl4, &_flkeys))
+       if (fib4_rules_early_flow_dissect(net, skb, &fl4, &_flkeys)) {
                flkeys = &_flkeys;
+       } else {
+               fl4.flowi4_proto = 0;
+               fl4.fl4_sport = 0;
+               fl4.fl4_dport = 0;
+       }
 
        err = fib_lookup(net, &fl4, res, 0);
        if (err != 0) {
@@ -2569,11 +2574,10 @@ struct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4,
 EXPORT_SYMBOL_GPL(ip_route_output_flow);
 
 /* called with rcu_read_lock held */
-static int rt_fill_info(struct net *net,  __be32 dst, __be32 src, u32 table_id,
-                       struct flowi4 *fl4, struct sk_buff *skb, u32 portid,
-                       u32 seq)
+static int rt_fill_info(struct net *net, __be32 dst, __be32 src,
+                       struct rtable *rt, u32 table_id, struct flowi4 *fl4,
+                       struct sk_buff *skb, u32 portid, u32 seq)
 {
-       struct rtable *rt = skb_rtable(skb);
        struct rtmsg *r;
        struct nlmsghdr *nlh;
        unsigned long expires = 0;
@@ -2669,7 +2673,7 @@ static int rt_fill_info(struct net *net,  __be32 dst, __be32 src, u32 table_id,
                        }
                } else
 #endif
-                       if (nla_put_u32(skb, RTA_IIF, skb->dev->ifindex))
+                       if (nla_put_u32(skb, RTA_IIF, fl4->flowi4_iif))
                                goto nla_put_failure;
        }
 
@@ -2684,43 +2688,93 @@ nla_put_failure:
        return -EMSGSIZE;
 }
 
+static struct sk_buff *inet_rtm_getroute_build_skb(__be32 src, __be32 dst,
+                                                  u8 ip_proto, __be16 sport,
+                                                  __be16 dport)
+{
+       struct sk_buff *skb;
+       struct iphdr *iph;
+
+       skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!skb)
+               return NULL;
+
+       /* Reserve room for dummy headers, this skb can pass
+        * through good chunk of routing engine.
+        */
+       skb_reset_mac_header(skb);
+       skb_reset_network_header(skb);
+       skb->protocol = htons(ETH_P_IP);
+       iph = skb_put(skb, sizeof(struct iphdr));
+       iph->protocol = ip_proto;
+       iph->saddr = src;
+       iph->daddr = dst;
+       iph->version = 0x4;
+       iph->frag_off = 0;
+       iph->ihl = 0x5;
+       skb_set_transport_header(skb, skb->len);
+
+       switch (iph->protocol) {
+       case IPPROTO_UDP: {
+               struct udphdr *udph;
+
+               udph = skb_put_zero(skb, sizeof(struct udphdr));
+               udph->source = sport;
+               udph->dest = dport;
+               udph->len = sizeof(struct udphdr);
+               udph->check = 0;
+               break;
+       }
+       case IPPROTO_TCP: {
+               struct tcphdr *tcph;
+
+               tcph = skb_put_zero(skb, sizeof(struct tcphdr));
+               tcph->source    = sport;
+               tcph->dest      = dport;
+               tcph->doff      = sizeof(struct tcphdr) / 4;
+               tcph->rst = 1;
+               tcph->check = ~tcp_v4_check(sizeof(struct tcphdr),
+                                           src, dst, 0);
+               break;
+       }
+       case IPPROTO_ICMP: {
+               struct icmphdr *icmph;
+
+               icmph = skb_put_zero(skb, sizeof(struct icmphdr));
+               icmph->type = ICMP_ECHO;
+               icmph->code = 0;
+       }
+       }
+
+       return skb;
+}
+
 static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
                             struct netlink_ext_ack *extack)
 {
        struct net *net = sock_net(in_skb->sk);
-       struct rtmsg *rtm;
        struct nlattr *tb[RTA_MAX+1];
+       u32 table_id = RT_TABLE_MAIN;
+       __be16 sport = 0, dport = 0;
        struct fib_result res = {};
+       u8 ip_proto = IPPROTO_UDP;
        struct rtable *rt = NULL;
+       struct sk_buff *skb;
+       struct rtmsg *rtm;
        struct flowi4 fl4;
        __be32 dst = 0;
        __be32 src = 0;
+       kuid_t uid;
        u32 iif;
        int err;
        int mark;
-       struct sk_buff *skb;
-       u32 table_id = RT_TABLE_MAIN;
-       kuid_t uid;
 
        err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy,
                          extack);
        if (err < 0)
-               goto errout;
+               return err;
 
        rtm = nlmsg_data(nlh);
-
-       skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
-       if (!skb) {
-               err = -ENOBUFS;
-               goto errout;
-       }
-
-       /* Reserve room for dummy headers, this skb can pass
-          through good chunk of routing engine.
-        */
-       skb_reset_mac_header(skb);
-       skb_reset_network_header(skb);
-
        src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0;
        dst = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0;
        iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0;
@@ -2730,14 +2784,22 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
        else
                uid = (iif ? INVALID_UID : current_uid());
 
-       /* Bugfix: need to give ip_route_input enough of an IP header to
-        * not gag.
-        */
-       ip_hdr(skb)->protocol = IPPROTO_UDP;
-       ip_hdr(skb)->saddr = src;
-       ip_hdr(skb)->daddr = dst;
+       if (tb[RTA_IP_PROTO]) {
+               err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
+                                                 &ip_proto, extack);
+               if (err)
+                       return err;
+       }
+
+       if (tb[RTA_SPORT])
+               sport = nla_get_be16(tb[RTA_SPORT]);
 
-       skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr));
+       if (tb[RTA_DPORT])
+               dport = nla_get_be16(tb[RTA_DPORT]);
+
+       skb = inet_rtm_getroute_build_skb(src, dst, ip_proto, sport, dport);
+       if (!skb)
+               return -ENOBUFS;
 
        memset(&fl4, 0, sizeof(fl4));
        fl4.daddr = dst;
@@ -2746,6 +2808,11 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
        fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0;
        fl4.flowi4_mark = mark;
        fl4.flowi4_uid = uid;
+       if (sport)
+               fl4.fl4_sport = sport;
+       if (dport)
+               fl4.fl4_dport = dport;
+       fl4.flowi4_proto = ip_proto;
 
        rcu_read_lock();
 
@@ -2755,10 +2822,10 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
                dev = dev_get_by_index_rcu(net, iif);
                if (!dev) {
                        err = -ENODEV;
-                       goto errout_free;
+                       goto errout_rcu;
                }
 
-               skb->protocol   = htons(ETH_P_IP);
+               fl4.flowi4_iif = iif; /* for rt_fill_info */
                skb->dev        = dev;
                skb->mark       = mark;
                err = ip_route_input_rcu(skb, dst, src, rtm->rtm_tos,
@@ -2778,7 +2845,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
        }
 
        if (err)
-               goto errout_free;
+               goto errout_rcu;
 
        if (rtm->rtm_flags & RTM_F_NOTIFY)
                rt->rt_flags |= RTCF_NOTIFY;
@@ -2786,34 +2853,40 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
        if (rtm->rtm_flags & RTM_F_LOOKUP_TABLE)
                table_id = res.table ? res.table->tb_id : 0;
 
+       /* reset skb for netlink reply msg */
+       skb_trim(skb, 0);
+       skb_reset_network_header(skb);
+       skb_reset_transport_header(skb);
+       skb_reset_mac_header(skb);
+
        if (rtm->rtm_flags & RTM_F_FIB_MATCH) {
                if (!res.fi) {
                        err = fib_props[res.type].error;
                        if (!err)
                                err = -EHOSTUNREACH;
-                       goto errout_free;
+                       goto errout_rcu;
                }
                err = fib_dump_info(skb, NETLINK_CB(in_skb).portid,
                                    nlh->nlmsg_seq, RTM_NEWROUTE, table_id,
                                    rt->rt_type, res.prefix, res.prefixlen,
                                    fl4.flowi4_tos, res.fi, 0);
        } else {
-               err = rt_fill_info(net, dst, src, table_id, &fl4, skb,
+               err = rt_fill_info(net, dst, src, rt, table_id, &fl4, skb,
                                   NETLINK_CB(in_skb).portid, nlh->nlmsg_seq);
        }
        if (err < 0)
-               goto errout_free;
+               goto errout_rcu;
 
        rcu_read_unlock();
 
        err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
-errout:
-       return err;
 
 errout_free:
+       return err;
+errout_rcu:
        rcu_read_unlock();
        kfree_skb(skb);
-       goto errout;
+       goto errout_free;
 }
 
 void ip_rt_multicast_event(struct in_device *in_dev)
index 4b195ba..d2eed3d 100644 (file)
@@ -46,6 +46,7 @@ static int tcp_syn_retries_min = 1;
 static int tcp_syn_retries_max = MAX_TCP_SYNCNT;
 static int ip_ping_group_range_min[] = { 0, 0 };
 static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX };
+static int comp_sack_nr_max = 255;
 
 /* obsolete */
 static int sysctl_tcp_low_latency __read_mostly;
@@ -1151,6 +1152,22 @@ static struct ctl_table ipv4_net_table[] = {
                .proc_handler   = proc_dointvec_minmax,
                .extra1         = &one,
        },
+       {
+               .procname       = "tcp_comp_sack_delay_ns",
+               .data           = &init_net.ipv4.sysctl_tcp_comp_sack_delay_ns,
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
+       {
+               .procname       = "tcp_comp_sack_nr",
+               .data           = &init_net.ipv4.sysctl_tcp_comp_sack_nr,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &comp_sack_nr_max,
+       },
        {
                .procname       = "udp_rmem_min",
                .data           = &init_net.ipv4.sysctl_udp_rmem_min,
index 62b776f..0a2ea0b 100644 (file)
@@ -2595,6 +2595,7 @@ int tcp_disconnect(struct sock *sk, int flags)
        dst_release(sk->sk_rx_dst);
        sk->sk_rx_dst = NULL;
        tcp_saved_syn_free(tp);
+       tp->compressed_ack = 0;
 
        /* Clean up fastopen related fields */
        tcp_free_fastopen_req(tp);
index b188e0d..1191cac 100644 (file)
@@ -203,21 +203,23 @@ static void tcp_measure_rcv_mss(struct sock *sk, const struct sk_buff *skb)
        }
 }
 
-static void tcp_incr_quickack(struct sock *sk)
+static void tcp_incr_quickack(struct sock *sk, unsigned int max_quickacks)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        unsigned int quickacks = tcp_sk(sk)->rcv_wnd / (2 * icsk->icsk_ack.rcv_mss);
 
        if (quickacks == 0)
                quickacks = 2;
+       quickacks = min(quickacks, max_quickacks);
        if (quickacks > icsk->icsk_ack.quick)
-               icsk->icsk_ack.quick = min(quickacks, TCP_MAX_QUICKACKS);
+               icsk->icsk_ack.quick = quickacks;
 }
 
-static void tcp_enter_quickack_mode(struct sock *sk)
+static void tcp_enter_quickack_mode(struct sock *sk, unsigned int max_quickacks)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
-       tcp_incr_quickack(sk);
+
+       tcp_incr_quickack(sk, max_quickacks);
        icsk->icsk_ack.pingpong = 0;
        icsk->icsk_ack.ato = TCP_ATO_MIN;
 }
@@ -261,7 +263,7 @@ static void __tcp_ecn_check_ce(struct tcp_sock *tp, const struct sk_buff *skb)
                 * it is probably a retransmit.
                 */
                if (tp->ecn_flags & TCP_ECN_SEEN)
-                       tcp_enter_quickack_mode((struct sock *)tp);
+                       tcp_enter_quickack_mode((struct sock *)tp, 1);
                break;
        case INET_ECN_CE:
                if (tcp_ca_needs_ecn((struct sock *)tp))
@@ -269,7 +271,7 @@ static void __tcp_ecn_check_ce(struct tcp_sock *tp, const struct sk_buff *skb)
 
                if (!(tp->ecn_flags & TCP_ECN_DEMAND_CWR)) {
                        /* Better not delay acks, sender can have a very low cwnd */
-                       tcp_enter_quickack_mode((struct sock *)tp);
+                       tcp_enter_quickack_mode((struct sock *)tp, 1);
                        tp->ecn_flags |= TCP_ECN_DEMAND_CWR;
                }
                tp->ecn_flags |= TCP_ECN_SEEN;
@@ -686,7 +688,7 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)
                /* The _first_ data packet received, initialize
                 * delayed ACK engine.
                 */
-               tcp_incr_quickack(sk);
+               tcp_incr_quickack(sk, TCP_MAX_QUICKACKS);
                icsk->icsk_ack.ato = TCP_ATO_MIN;
        } else {
                int m = now - icsk->icsk_ack.lrcvtime;
@@ -702,7 +704,7 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb)
                        /* Too long gap. Apparently sender failed to
                         * restart window, so that we send ACKs quickly.
                         */
-                       tcp_incr_quickack(sk);
+                       tcp_incr_quickack(sk, TCP_MAX_QUICKACKS);
                        sk_mem_reclaim(sk);
                }
        }
@@ -1917,19 +1919,54 @@ static inline void tcp_init_undo(struct tcp_sock *tp)
        tp->undo_retrans = tp->retrans_out ? : -1;
 }
 
-/* Enter Loss state. If we detect SACK reneging, forget all SACK information
+static bool tcp_is_rack(const struct sock *sk)
+{
+       return sock_net(sk)->ipv4.sysctl_tcp_recovery & TCP_RACK_LOSS_DETECTION;
+}
+
+/* If we detect SACK reneging, forget all SACK information
  * and reset tags completely, otherwise preserve SACKs. If receiver
  * dropped its ofo queue, we will know this due to reneging detection.
  */
+static void tcp_timeout_mark_lost(struct sock *sk)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       struct sk_buff *skb, *head;
+       bool is_reneg;                  /* is receiver reneging on SACKs? */
+
+       head = tcp_rtx_queue_head(sk);
+       is_reneg = head && (TCP_SKB_CB(head)->sacked & TCPCB_SACKED_ACKED);
+       if (is_reneg) {
+               NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSACKRENEGING);
+               tp->sacked_out = 0;
+               /* Mark SACK reneging until we recover from this loss event. */
+               tp->is_sack_reneg = 1;
+       } else if (tcp_is_reno(tp)) {
+               tcp_reset_reno_sack(tp);
+       }
+
+       skb = head;
+       skb_rbtree_walk_from(skb) {
+               if (is_reneg)
+                       TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_ACKED;
+               else if (tcp_is_rack(sk) && skb != head &&
+                        tcp_rack_skb_timeout(tp, skb, 0) > 0)
+                       continue; /* Don't mark recently sent ones lost yet */
+               tcp_mark_skb_lost(sk, skb);
+       }
+       tcp_verify_left_out(tp);
+       tcp_clear_all_retrans_hints(tp);
+}
+
+/* Enter Loss state. */
 void tcp_enter_loss(struct sock *sk)
 {
        const struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        struct net *net = sock_net(sk);
-       struct sk_buff *skb;
        bool new_recovery = icsk->icsk_ca_state < TCP_CA_Recovery;
-       bool is_reneg;                  /* is receiver reneging on SACKs? */
-       bool mark_lost;
+
+       tcp_timeout_mark_lost(sk);
 
        /* Reduce ssthresh if it has not yet been made inside this window. */
        if (icsk->icsk_ca_state <= TCP_CA_Disorder ||
@@ -1941,40 +1978,10 @@ void tcp_enter_loss(struct sock *sk)
                tcp_ca_event(sk, CA_EVENT_LOSS);
                tcp_init_undo(tp);
        }
-       tp->snd_cwnd       = 1;
+       tp->snd_cwnd       = tcp_packets_in_flight(tp) + 1;
        tp->snd_cwnd_cnt   = 0;
        tp->snd_cwnd_stamp = tcp_jiffies32;
 
-       tp->retrans_out = 0;
-       tp->lost_out = 0;
-
-       if (tcp_is_reno(tp))
-               tcp_reset_reno_sack(tp);
-
-       skb = tcp_rtx_queue_head(sk);
-       is_reneg = skb && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED);
-       if (is_reneg) {
-               NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSACKRENEGING);
-               tp->sacked_out = 0;
-               /* Mark SACK reneging until we recover from this loss event. */
-               tp->is_sack_reneg = 1;
-       }
-       tcp_clear_all_retrans_hints(tp);
-
-       skb_rbtree_walk_from(skb) {
-               mark_lost = (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED) ||
-                            is_reneg);
-               if (mark_lost)
-                       tcp_sum_lost(tp, skb);
-               TCP_SKB_CB(skb)->sacked &= (~TCPCB_TAGBITS)|TCPCB_SACKED_ACKED;
-               if (mark_lost) {
-                       TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_ACKED;
-                       TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
-                       tp->lost_out += tcp_skb_pcount(skb);
-               }
-       }
-       tcp_verify_left_out(tp);
-
        /* Timeout in disordered state after receiving substantial DUPACKs
         * suggests that the degree of reordering is over-estimated.
         */
@@ -2141,7 +2148,7 @@ static bool tcp_time_to_recover(struct sock *sk, int flag)
                return true;
 
        /* Not-A-Trick#2 : Classic rule... */
-       if (tcp_dupack_heuristics(tp) > tp->reordering)
+       if (!tcp_is_rack(sk) && tcp_dupack_heuristics(tp) > tp->reordering)
                return true;
 
        return false;
@@ -2218,9 +2225,7 @@ static void tcp_update_scoreboard(struct sock *sk, int fast_rexmit)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (tcp_is_reno(tp)) {
-               tcp_mark_head_lost(sk, 1, 1);
-       } else {
+       if (tcp_is_sack(tp)) {
                int sacked_upto = tp->sacked_out - tp->reordering;
                if (sacked_upto >= 0)
                        tcp_mark_head_lost(sk, sacked_upto, 0);
@@ -2718,12 +2723,16 @@ static bool tcp_try_undo_partial(struct sock *sk, u32 prior_snd_una)
        return false;
 }
 
-static void tcp_rack_identify_loss(struct sock *sk, int *ack_flag)
+static void tcp_identify_packet_loss(struct sock *sk, int *ack_flag)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       /* Use RACK to detect loss */
-       if (sock_net(sk)->ipv4.sysctl_tcp_recovery & TCP_RACK_LOSS_DETECTION) {
+       if (tcp_rtx_queue_empty(sk))
+               return;
+
+       if (unlikely(tcp_is_reno(tp))) {
+               tcp_newreno_mark_lost(sk, *ack_flag & FLAG_SND_UNA_ADVANCED);
+       } else if (tcp_is_rack(sk)) {
                u32 prior_retrans = tp->retrans_out;
 
                tcp_rack_mark_lost(sk);
@@ -2819,11 +2828,11 @@ static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
                        tcp_try_keep_open(sk);
                        return;
                }
-               tcp_rack_identify_loss(sk, ack_flag);
+               tcp_identify_packet_loss(sk, ack_flag);
                break;
        case TCP_CA_Loss:
                tcp_process_loss(sk, flag, is_dupack, rexmit);
-               tcp_rack_identify_loss(sk, ack_flag);
+               tcp_identify_packet_loss(sk, ack_flag);
                if (!(icsk->icsk_ca_state == TCP_CA_Open ||
                      (*ack_flag & FLAG_LOST_RETRANS)))
                        return;
@@ -2840,7 +2849,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
                if (icsk->icsk_ca_state <= TCP_CA_Disorder)
                        tcp_try_undo_dsack(sk);
 
-               tcp_rack_identify_loss(sk, ack_flag);
+               tcp_identify_packet_loss(sk, ack_flag);
                if (!tcp_time_to_recover(sk, flag)) {
                        tcp_try_to_open(sk, flag);
                        return;
@@ -2862,7 +2871,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una,
                fast_rexmit = 1;
        }
 
-       if (do_lost)
+       if (!tcp_is_rack(sk) && do_lost)
                tcp_update_scoreboard(sk, fast_rexmit);
        *rexmit = REXMIT_LOST;
 }
@@ -4172,7 +4181,7 @@ static void tcp_send_dupack(struct sock *sk, const struct sk_buff *skb)
        if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
            before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
                NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOST);
-               tcp_enter_quickack_mode(sk);
+               tcp_enter_quickack_mode(sk, TCP_MAX_QUICKACKS);
 
                if (tcp_is_sack(tp) && sock_net(sk)->ipv4.sysctl_tcp_dsack) {
                        u32 end_seq = TCP_SKB_CB(skb)->end_seq;
@@ -4242,6 +4251,8 @@ 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)
+                       tcp_send_ack(sk);
                this_sack--;
                tp->rx_opt.num_sacks--;
                sp--;
@@ -4697,7 +4708,7 @@ queue_and_out:
                tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
 
 out_of_window:
-               tcp_enter_quickack_mode(sk);
+               tcp_enter_quickack_mode(sk, TCP_MAX_QUICKACKS);
                inet_csk_schedule_ack(sk);
 drop:
                tcp_drop(sk, skb);
@@ -4708,8 +4719,6 @@ drop:
        if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt + tcp_receive_window(tp)))
                goto out_of_window;
 
-       tcp_enter_quickack_mode(sk);
-
        if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
                /* Partial packet, seq < rcv_next < end_seq */
                SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X\n",
@@ -5076,6 +5085,7 @@ static inline void tcp_data_snd_check(struct sock *sk)
 static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible)
 {
        struct tcp_sock *tp = tcp_sk(sk);
+       unsigned long rtt, delay;
 
            /* More than one full frame received... */
        if (((tp->rcv_nxt - tp->rcv_wup) > inet_csk(sk)->icsk_ack.rcv_mss &&
@@ -5087,15 +5097,36 @@ static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible)
            (tp->rcv_nxt - tp->copied_seq < sk->sk_rcvlowat ||
             __tcp_select_window(sk) >= tp->rcv_wnd)) ||
            /* We ACK each frame or... */
-           tcp_in_quickack_mode(sk) ||
-           /* We have out of order data. */
-           (ofo_possible && !RB_EMPTY_ROOT(&tp->out_of_order_queue))) {
-               /* Then ack it now */
+           tcp_in_quickack_mode(sk)) {
+send_now:
                tcp_send_ack(sk);
-       } else {
-               /* Else, send delayed ack. */
+               return;
+       }
+
+       if (!ofo_possible || RB_EMPTY_ROOT(&tp->out_of_order_queue)) {
                tcp_send_delayed_ack(sk);
+               return;
        }
+
+       if (!tcp_is_sack(tp) ||
+           tp->compressed_ack >= sock_net(sk)->ipv4.sysctl_tcp_comp_sack_nr)
+               goto send_now;
+       tp->compressed_ack++;
+
+       if (hrtimer_is_queued(&tp->compressed_ack_timer))
+               return;
+
+       /* compress ack timer : 5 % of rtt, but no more than tcp_comp_sack_delay_ns */
+
+       rtt = tp->rcv_rtt_est.rtt_us;
+       if (tp->srtt_us && tp->srtt_us < rtt)
+               rtt = tp->srtt_us;
+
+       delay = min_t(unsigned long, sock_net(sk)->ipv4.sysctl_tcp_comp_sack_delay_ns,
+                     rtt * (NSEC_PER_USEC >> 3)/20);
+       sock_hold(sk);
+       hrtimer_start(&tp->compressed_ack_timer, ns_to_ktime(delay),
+                     HRTIMER_MODE_REL_PINNED_SOFT);
 }
 
 static inline void tcp_ack_snd_check(struct sock *sk)
@@ -5761,7 +5792,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
                         * to stand against the temptation 8)     --ANK
                         */
                        inet_csk_schedule_ack(sk);
-                       tcp_enter_quickack_mode(sk);
+                       tcp_enter_quickack_mode(sk, TCP_MAX_QUICKACKS);
                        inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
                                                  TCP_DELACK_MAX, TCP_RTO_MAX);
 
index caf23de..adbdb50 100644 (file)
@@ -2572,6 +2572,8 @@ static int __net_init tcp_sk_init(struct net *net)
                       init_net.ipv4.sysctl_tcp_wmem,
                       sizeof(init_net.ipv4.sysctl_tcp_wmem));
        }
+       net->ipv4.sysctl_tcp_comp_sack_delay_ns = NSEC_PER_MSEC;
+       net->ipv4.sysctl_tcp_comp_sack_nr = 44;
        net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE;
        spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock);
        net->ipv4.sysctl_tcp_fastopen_blackhole_timeout = 60 * 60;
index 0d8f950..8e08b40 100644 (file)
@@ -162,6 +162,15 @@ static void tcp_event_data_sent(struct tcp_sock *tp,
 /* Account for an ACK we sent. */
 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)) {
+               NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPACKCOMPRESSED,
+                             tp->compressed_ack);
+               tp->compressed_ack = 0;
+               if (hrtimer_try_to_cancel(&tp->compressed_ack_timer) == 1)
+                       __sock_put(sk);
+       }
        tcp_dec_quickack_mode(sk, pkts);
        inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
 }
@@ -2814,8 +2823,10 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
                return -EBUSY;
 
        if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) {
-               if (before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
-                       BUG();
+               if (unlikely(before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))) {
+                       WARN_ON_ONCE(1);
+                       return -EINVAL;
+               }
                if (tcp_trim_head(sk, skb, tp->snd_una - TCP_SKB_CB(skb)->seq))
                        return -ENOMEM;
        }
@@ -3323,6 +3334,7 @@ static void tcp_connect_init(struct sock *sk)
        sock_reset_flag(sk, SOCK_DONE);
        tp->snd_wnd = 0;
        tcp_init_wl(tp, 0);
+       tcp_write_queue_purge(sk);
        tp->snd_una = tp->write_seq;
        tp->snd_sml = tp->write_seq;
        tp->snd_up = tp->write_seq;
index 3a81720..71593e4 100644 (file)
@@ -2,7 +2,7 @@
 #include <linux/tcp.h>
 #include <net/tcp.h>
 
-static void tcp_rack_mark_skb_lost(struct sock *sk, struct sk_buff *skb)
+void tcp_mark_skb_lost(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
@@ -21,6 +21,38 @@ static bool tcp_rack_sent_after(u64 t1, u64 t2, u32 seq1, u32 seq2)
        return t1 > t2 || (t1 == t2 && after(seq1, seq2));
 }
 
+static u32 tcp_rack_reo_wnd(const struct sock *sk)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if (!tp->rack.reord) {
+               /* If reordering has not been observed, be aggressive during
+                * the recovery or starting the recovery by DUPACK threshold.
+                */
+               if (inet_csk(sk)->icsk_ca_state >= TCP_CA_Recovery)
+                       return 0;
+
+               if (tp->sacked_out >= tp->reordering &&
+                   !(sock_net(sk)->ipv4.sysctl_tcp_recovery & TCP_RACK_NO_DUPTHRESH))
+                       return 0;
+       }
+
+       /* To be more reordering resilient, allow min_rtt/4 settling delay.
+        * Use min_rtt instead of the smoothed RTT because reordering is
+        * often a path property and less related to queuing or delayed ACKs.
+        * Upon receiving DSACKs, linearly increase the window up to the
+        * smoothed RTT.
+        */
+       return min((tcp_min_rtt(tp) >> 2) * tp->rack.reo_wnd_steps,
+                  tp->srtt_us >> 3);
+}
+
+s32 tcp_rack_skb_timeout(struct tcp_sock *tp, struct sk_buff *skb, u32 reo_wnd)
+{
+       return tp->rack.rtt_us + reo_wnd -
+              tcp_stamp_us_delta(tp->tcp_mstamp, skb->skb_mstamp);
+}
+
 /* RACK loss detection (IETF draft draft-ietf-tcpm-rack-01):
  *
  * Marks a packet lost, if some packet sent later has been (s)acked.
@@ -44,23 +76,11 @@ static bool tcp_rack_sent_after(u64 t1, u64 t2, u32 seq1, u32 seq2)
 static void tcp_rack_detect_loss(struct sock *sk, u32 *reo_timeout)
 {
        struct tcp_sock *tp = tcp_sk(sk);
-       u32 min_rtt = tcp_min_rtt(tp);
        struct sk_buff *skb, *n;
        u32 reo_wnd;
 
        *reo_timeout = 0;
-       /* To be more reordering resilient, allow min_rtt/4 settling delay
-        * (lower-bounded to 1000uS). We use min_rtt instead of the smoothed
-        * RTT because reordering is often a path property and less related
-        * to queuing or delayed ACKs.
-        */
-       reo_wnd = 1000;
-       if ((tp->rack.reord || inet_csk(sk)->icsk_ca_state < TCP_CA_Recovery) &&
-           min_rtt != ~0U) {
-               reo_wnd = max((min_rtt >> 2) * tp->rack.reo_wnd_steps, reo_wnd);
-               reo_wnd = min(reo_wnd, tp->srtt_us >> 3);
-       }
-
+       reo_wnd = tcp_rack_reo_wnd(sk);
        list_for_each_entry_safe(skb, n, &tp->tsorted_sent_queue,
                                 tcp_tsorted_anchor) {
                struct tcp_skb_cb *scb = TCP_SKB_CB(skb);
@@ -78,10 +98,9 @@ static void tcp_rack_detect_loss(struct sock *sk, u32 *reo_timeout)
                /* A packet is lost if it has not been s/acked beyond
                 * the recent RTT plus the reordering window.
                 */
-               remaining = tp->rack.rtt_us + reo_wnd -
-                           tcp_stamp_us_delta(tp->tcp_mstamp, skb->skb_mstamp);
+               remaining = tcp_rack_skb_timeout(tp, skb, reo_wnd);
                if (remaining <= 0) {
-                       tcp_rack_mark_skb_lost(sk, skb);
+                       tcp_mark_skb_lost(sk, skb);
                        list_del_init(&skb->tcp_tsorted_anchor);
                } else {
                        /* Record maximum wait time */
@@ -202,3 +221,30 @@ void tcp_rack_update_reo_wnd(struct sock *sk, struct rate_sample *rs)
                tp->rack.reo_wnd_steps = 1;
        }
 }
+
+/* RFC6582 NewReno recovery for non-SACK connection. It simply retransmits
+ * the next unacked packet upon receiving
+ * a) three or more DUPACKs to start the fast recovery
+ * b) an ACK acknowledging new data during the fast recovery.
+ */
+void tcp_newreno_mark_lost(struct sock *sk, bool snd_una_advanced)
+{
+       const u8 state = inet_csk(sk)->icsk_ca_state;
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if ((state < TCP_CA_Recovery && tp->sacked_out >= tp->reordering) ||
+           (state == TCP_CA_Recovery && snd_una_advanced)) {
+               struct sk_buff *skb = tcp_rtx_queue_head(sk);
+               u32 mss;
+
+               if (TCP_SKB_CB(skb)->sacked & TCPCB_LOST)
+                       return;
+
+               mss = tcp_skb_mss(skb);
+               if (tcp_skb_pcount(skb) > 1 && skb->len > mss)
+                       tcp_fragment(sk, TCP_FRAG_IN_RTX_QUEUE, skb,
+                                    mss, mss, GFP_ATOMIC);
+
+               tcp_skb_mark_lost_uncond_verify(tp, skb);
+       }
+}
index 92bdf64..3b36117 100644 (file)
@@ -708,6 +708,27 @@ out:
        sock_put(sk);
 }
 
+static enum hrtimer_restart tcp_compressed_ack_kick(struct hrtimer *timer)
+{
+       struct tcp_sock *tp = container_of(timer, struct tcp_sock, compressed_ack_timer);
+       struct sock *sk = (struct sock *)tp;
+
+       bh_lock_sock(sk);
+       if (!sock_owned_by_user(sk)) {
+               if (tp->compressed_ack)
+                       tcp_send_ack(sk);
+       } else {
+               if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED,
+                                     &sk->sk_tsq_flags))
+                       sock_hold(sk);
+       }
+       bh_unlock_sock(sk);
+
+       sock_put(sk);
+
+       return HRTIMER_NORESTART;
+}
+
 void tcp_init_xmit_timers(struct sock *sk)
 {
        inet_csk_init_xmit_timers(sk, &tcp_write_timer, &tcp_delack_timer,
@@ -715,4 +736,8 @@ void tcp_init_xmit_timers(struct sock *sk)
        hrtimer_init(&tcp_sk(sk)->pacing_timer, CLOCK_MONOTONIC,
                     HRTIMER_MODE_ABS_PINNED_SOFT);
        tcp_sk(sk)->pacing_timer.function = tcp_pace_kick;
+
+       hrtimer_init(&tcp_sk(sk)->compressed_ack_timer, CLOCK_MONOTONIC,
+                    HRTIMER_MODE_REL_PINNED_SOFT);
+       tcp_sk(sk)->compressed_ack_timer.function = tcp_compressed_ack_kick;
 }
index ff4d4ba..d71f1f3 100644 (file)
@@ -788,7 +788,8 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
                        return -EINVAL;
                if (sk->sk_no_check_tx)
                        return -EINVAL;
-               if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite)
+               if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite ||
+                   dst_xfrm(skb_dst(skb)))
                        return -EIO;
 
                skb_shinfo(skb)->gso_size = cork->gso_size;
index 32b564d..2fe754f 100644 (file)
@@ -134,8 +134,39 @@ static int eafnosupport_ipv6_dst_lookup(struct net *net, struct sock *u1,
        return -EAFNOSUPPORT;
 }
 
+static struct fib6_table *eafnosupport_fib6_get_table(struct net *net, u32 id)
+{
+       return NULL;
+}
+
+static struct fib6_info *
+eafnosupport_fib6_table_lookup(struct net *net, struct fib6_table *table,
+                              int oif, struct flowi6 *fl6, int flags)
+{
+       return NULL;
+}
+
+static struct fib6_info *
+eafnosupport_fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
+                        int flags)
+{
+       return NULL;
+}
+
+static struct fib6_info *
+eafnosupport_fib6_multipath_select(const struct net *net, struct fib6_info *f6i,
+                                  struct flowi6 *fl6, int oif,
+                                  const struct sk_buff *skb, int strict)
+{
+       return f6i;
+}
+
 const struct ipv6_stub *ipv6_stub __read_mostly = &(struct ipv6_stub) {
-       .ipv6_dst_lookup = eafnosupport_ipv6_dst_lookup,
+       .ipv6_dst_lookup   = eafnosupport_ipv6_dst_lookup,
+       .fib6_get_table    = eafnosupport_fib6_get_table,
+       .fib6_table_lookup = eafnosupport_fib6_table_lookup,
+       .fib6_lookup       = eafnosupport_fib6_lookup,
+       .fib6_multipath_select = eafnosupport_fib6_multipath_select,
 };
 EXPORT_SYMBOL_GPL(ipv6_stub);
 
index d0af96e..50de8b0 100644 (file)
@@ -889,7 +889,11 @@ static struct pernet_operations inet6_net_ops = {
 static const struct ipv6_stub ipv6_stub_impl = {
        .ipv6_sock_mc_join = ipv6_sock_mc_join,
        .ipv6_sock_mc_drop = ipv6_sock_mc_drop,
-       .ipv6_dst_lookup = ip6_dst_lookup,
+       .ipv6_dst_lookup   = ip6_dst_lookup,
+       .fib6_get_table    = fib6_get_table,
+       .fib6_table_lookup = fib6_table_lookup,
+       .fib6_lookup       = fib6_lookup,
+       .fib6_multipath_select = fib6_multipath_select,
        .udpv6_encap_enable = udpv6_encap_enable,
        .ndisc_send_na = ndisc_send_na,
        .nd_tbl = &nd_tbl,
index 6547fc6..f590446 100644 (file)
@@ -60,6 +60,39 @@ unsigned int fib6_rules_seq_read(struct net *net)
        return fib_rules_seq_read(net, AF_INET6);
 }
 
+/* called with rcu lock held; no reference taken on fib6_info */
+struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
+                             int flags)
+{
+       struct fib6_info *f6i;
+       int err;
+
+       if (net->ipv6.fib6_has_custom_rules) {
+               struct fib_lookup_arg arg = {
+                       .lookup_ptr = fib6_table_lookup,
+                       .lookup_data = &oif,
+                       .flags = FIB_LOOKUP_NOREF,
+               };
+
+               l3mdev_update_flow(net, flowi6_to_flowi(fl6));
+
+               err = fib_rules_lookup(net->ipv6.fib6_rules_ops,
+                                      flowi6_to_flowi(fl6), flags, &arg);
+               if (err)
+                       return ERR_PTR(err);
+
+               f6i = arg.result ? : net->ipv6.fib6_null_entry;
+       } else {
+               f6i = fib6_table_lookup(net, net->ipv6.fib6_local_tbl,
+                                       oif, fl6, flags);
+               if (!f6i || f6i == net->ipv6.fib6_null_entry)
+                       f6i = fib6_table_lookup(net, net->ipv6.fib6_main_tbl,
+                                               oif, fl6, flags);
+       }
+
+       return f6i;
+}
+
 struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
                                   const struct sk_buff *skb,
                                   int flags, pol_lookup_t lookup)
@@ -96,8 +129,73 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
        return &net->ipv6.ip6_null_entry->dst;
 }
 
-static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
-                           int flags, struct fib_lookup_arg *arg)
+static int fib6_rule_saddr(struct net *net, struct fib_rule *rule, int flags,
+                          struct flowi6 *flp6, const struct net_device *dev)
+{
+       struct fib6_rule *r = (struct fib6_rule *)rule;
+
+       /* If we need to find a source address for this traffic,
+        * we check the result if it meets requirement of the rule.
+        */
+       if ((rule->flags & FIB_RULE_FIND_SADDR) &&
+           r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) {
+               struct in6_addr saddr;
+
+               if (ipv6_dev_get_saddr(net, dev, &flp6->daddr,
+                                      rt6_flags2srcprefs(flags), &saddr))
+                       return -EAGAIN;
+
+               if (!ipv6_prefix_equal(&saddr, &r->src.addr, r->src.plen))
+                       return -EAGAIN;
+
+               flp6->saddr = saddr;
+       }
+
+       return 0;
+}
+
+static int fib6_rule_action_alt(struct fib_rule *rule, struct flowi *flp,
+                               int flags, struct fib_lookup_arg *arg)
+{
+       struct flowi6 *flp6 = &flp->u.ip6;
+       struct net *net = rule->fr_net;
+       struct fib6_table *table;
+       struct fib6_info *f6i;
+       int err = -EAGAIN, *oif;
+       u32 tb_id;
+
+       switch (rule->action) {
+       case FR_ACT_TO_TBL:
+               break;
+       case FR_ACT_UNREACHABLE:
+               return -ENETUNREACH;
+       case FR_ACT_PROHIBIT:
+               return -EACCES;
+       case FR_ACT_BLACKHOLE:
+       default:
+               return -EINVAL;
+       }
+
+       tb_id = fib_rule_get_table(rule, arg);
+       table = fib6_get_table(net, tb_id);
+       if (!table)
+               return -EAGAIN;
+
+       oif = (int *)arg->lookup_data;
+       f6i = fib6_table_lookup(net, table, *oif, flp6, flags);
+       if (f6i != net->ipv6.fib6_null_entry) {
+               err = fib6_rule_saddr(net, rule, flags, flp6,
+                                     fib6_info_nh_dev(f6i));
+
+               if (likely(!err))
+                       arg->result = f6i;
+       }
+
+       return err;
+}
+
+static int __fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
+                             int flags, struct fib_lookup_arg *arg)
 {
        struct flowi6 *flp6 = &flp->u.ip6;
        struct rt6_info *rt = NULL;
@@ -134,27 +232,12 @@ static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
 
        rt = lookup(net, table, flp6, arg->lookup_data, flags);
        if (rt != net->ipv6.ip6_null_entry) {
-               struct fib6_rule *r = (struct fib6_rule *)rule;
-
-               /*
-                * If we need to find a source address for this traffic,
-                * we check the result if it meets requirement of the rule.
-                */
-               if ((rule->flags & FIB_RULE_FIND_SADDR) &&
-                   r->src.plen && !(flags & RT6_LOOKUP_F_HAS_SADDR)) {
-                       struct in6_addr saddr;
-
-                       if (ipv6_dev_get_saddr(net,
-                                              ip6_dst_idev(&rt->dst)->dev,
-                                              &flp6->daddr,
-                                              rt6_flags2srcprefs(flags),
-                                              &saddr))
-                               goto again;
-                       if (!ipv6_prefix_equal(&saddr, &r->src.addr,
-                                              r->src.plen))
-                               goto again;
-                       flp6->saddr = saddr;
-               }
+               err = fib6_rule_saddr(net, rule, flags, flp6,
+                                     ip6_dst_idev(&rt->dst)->dev);
+
+               if (err == -EAGAIN)
+                       goto again;
+
                err = rt->dst.error;
                if (err != -EAGAIN)
                        goto out;
@@ -172,6 +255,15 @@ out:
        return err;
 }
 
+static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
+                           int flags, struct fib_lookup_arg *arg)
+{
+       if (arg->lookup_ptr == fib6_table_lookup)
+               return fib6_rule_action_alt(rule, flp, flags, arg);
+
+       return __fib6_rule_action(rule, flp, flags, arg);
+}
+
 static bool fib6_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg)
 {
        struct rt6_info *rt = (struct rt6_info *) arg->result;
index f0a4262..f9132a6 100644 (file)
@@ -354,6 +354,13 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
        return &rt->dst;
 }
 
+/* called with rcu lock held; no reference taken on fib6_info */
+struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
+                             int flags)
+{
+       return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6, flags);
+}
+
 static void __net_init fib6_tables_init(struct net *net)
 {
        fib6_link_table(net, net->ipv6.fib6_main_tbl);
@@ -927,19 +934,19 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
 {
        struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
                                    lockdep_is_held(&rt->fib6_table->tb6_lock));
-       struct fib6_info *iter = NULL;
+       struct fib6_info *iter = NULL, *match = NULL;
        struct fib6_info __rcu **ins;
-       struct fib6_info __rcu **fallback_ins = NULL;
        int replace = (info->nlh &&
                       (info->nlh->nlmsg_flags & NLM_F_REPLACE));
+       int append = (info->nlh &&
+                      (info->nlh->nlmsg_flags & NLM_F_APPEND));
        int add = (!info->nlh ||
                   (info->nlh->nlmsg_flags & NLM_F_CREATE));
        int found = 0;
-       bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
        u16 nlflags = NLM_F_EXCL;
        int err;
 
-       if (info->nlh && (info->nlh->nlmsg_flags & NLM_F_APPEND))
+       if (append)
                nlflags |= NLM_F_APPEND;
 
        ins = &fn->leaf;
@@ -961,13 +968,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
 
                        nlflags &= ~NLM_F_EXCL;
                        if (replace) {
-                               if (rt_can_ecmp == rt6_qualify_for_ecmp(iter)) {
-                                       found++;
-                                       break;
-                               }
-                               if (rt_can_ecmp)
-                                       fallback_ins = fallback_ins ?: ins;
-                               goto next_iter;
+                               found++;
+                               break;
                        }
 
                        if (rt6_duplicate_nexthop(iter, rt)) {
@@ -982,86 +984,67 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
                                fib6_metric_set(iter, RTAX_MTU, rt->fib6_pmtu);
                                return -EEXIST;
                        }
-                       /* If we have the same destination and the same metric,
-                        * but not the same gateway, then the route we try to
-                        * add is sibling to this route, increment our counter
-                        * of siblings, and later we will add our route to the
-                        * list.
-                        * Only static routes (which don't have flag
-                        * RTF_EXPIRES) are used for ECMPv6.
-                        *
-                        * To avoid long list, we only had siblings if the
-                        * route have a gateway.
-                        */
-                       if (rt_can_ecmp &&
-                           rt6_qualify_for_ecmp(iter))
-                               rt->fib6_nsiblings++;
+
+                       /* first route that matches */
+                       if (!match)
+                               match = iter;
                }
 
                if (iter->fib6_metric > rt->fib6_metric)
                        break;
 
-next_iter:
                ins = &iter->fib6_next;
        }
 
-       if (fallback_ins && !found) {
-               /* No ECMP-able route found, replace first non-ECMP one */
-               ins = fallback_ins;
-               iter = rcu_dereference_protected(*ins,
-                                   lockdep_is_held(&rt->fib6_table->tb6_lock));
-               found++;
-       }
-
        /* Reset round-robin state, if necessary */
        if (ins == &fn->leaf)
                fn->rr_ptr = NULL;
 
        /* Link this route to others same route. */
-       if (rt->fib6_nsiblings) {
-               unsigned int fib6_nsiblings;
+       if (append && match) {
                struct fib6_info *sibling, *temp_sibling;
 
-               /* Find the first route that have the same metric */
-               sibling = leaf;
-               while (sibling) {
-                       if (sibling->fib6_metric == rt->fib6_metric &&
-                           rt6_qualify_for_ecmp(sibling)) {
-                               list_add_tail(&rt->fib6_siblings,
-                                             &sibling->fib6_siblings);
-                               break;
-                       }
-                       sibling = rcu_dereference_protected(sibling->fib6_next,
-                                   lockdep_is_held(&rt->fib6_table->tb6_lock));
+               if (rt->fib6_flags & RTF_REJECT) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Can not append a REJECT route");
+                       return -EINVAL;
+               } else if (match->fib6_flags & RTF_REJECT) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Can not append to a REJECT route");
+                       return -EINVAL;
                }
+               rt->fib6_nsiblings = match->fib6_nsiblings;
+               list_add_tail(&rt->fib6_siblings, &match->fib6_siblings);
+               match->fib6_nsiblings++;
+
                /* For each sibling in the list, increment the counter of
                 * siblings. BUG() if counters does not match, list of siblings
                 * is broken!
                 */
-               fib6_nsiblings = 0;
                list_for_each_entry_safe(sibling, temp_sibling,
-                                        &rt->fib6_siblings, fib6_siblings) {
+                                        &match->fib6_siblings, fib6_siblings) {
                        sibling->fib6_nsiblings++;
-                       BUG_ON(sibling->fib6_nsiblings != rt->fib6_nsiblings);
-                       fib6_nsiblings++;
+                       BUG_ON(sibling->fib6_nsiblings != match->fib6_nsiblings);
                }
-               BUG_ON(fib6_nsiblings != rt->fib6_nsiblings);
-               rt6_multipath_rebalance(temp_sibling);
+
+               rt6_multipath_rebalance(match);
        }
 
        /*
         *      insert node
         */
        if (!replace) {
+               enum fib_event_type event;
+
                if (!add)
                        pr_warn("NLM_F_CREATE should be set when creating new route\n");
 
 add:
                nlflags |= NLM_F_CREATE;
 
-               err = call_fib6_entry_notifiers(info->nl_net,
-                                               FIB_EVENT_ENTRY_ADD,
-                                               rt, extack);
+               event = append ? FIB_EVENT_ENTRY_APPEND : FIB_EVENT_ENTRY_ADD;
+               err = call_fib6_entry_notifiers(info->nl_net, event, rt,
+                                               extack);
                if (err)
                        return err;
 
@@ -1079,7 +1062,7 @@ add:
                }
 
        } else {
-               int nsiblings;
+               struct fib6_info *tmp;
 
                if (!found) {
                        if (add)
@@ -1094,48 +1077,57 @@ add:
                if (err)
                        return err;
 
+               /* if route being replaced has siblings, set tmp to
+                * last one, otherwise tmp is current route. this is
+                * used to set fib6_next for new route
+                */
+               if (iter->fib6_nsiblings)
+                       tmp = list_last_entry(&iter->fib6_siblings,
+                                             struct fib6_info,
+                                             fib6_siblings);
+               else
+                       tmp = iter;
+
+               /* insert new route */
                atomic_inc(&rt->fib6_ref);
                rcu_assign_pointer(rt->fib6_node, fn);
-               rt->fib6_next = iter->fib6_next;
+               rt->fib6_next = tmp->fib6_next;
                rcu_assign_pointer(*ins, rt);
+
                if (!info->skip_notify)
                        inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE);
                if (!(fn->fn_flags & RTN_RTINFO)) {
                        info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
                        fn->fn_flags |= RTN_RTINFO;
                }
-               nsiblings = iter->fib6_nsiblings;
-               iter->fib6_node = NULL;
-               fib6_purge_rt(iter, fn, info->nl_net);
-               if (rcu_access_pointer(fn->rr_ptr) == iter)
-                       fn->rr_ptr = NULL;
-               fib6_info_release(iter);
 
-               if (nsiblings) {
+               /* delete old route */
+               rt = iter;
+
+               if (rt->fib6_nsiblings) {
+                       struct fib6_info *tmp;
+
                        /* Replacing an ECMP route, remove all siblings */
-                       ins = &rt->fib6_next;
-                       iter = rcu_dereference_protected(*ins,
-                                   lockdep_is_held(&rt->fib6_table->tb6_lock));
-                       while (iter) {
-                               if (iter->fib6_metric > rt->fib6_metric)
-                                       break;
-                               if (rt6_qualify_for_ecmp(iter)) {
-                                       *ins = iter->fib6_next;
-                                       iter->fib6_node = NULL;
-                                       fib6_purge_rt(iter, fn, info->nl_net);
-                                       if (rcu_access_pointer(fn->rr_ptr) == iter)
-                                               fn->rr_ptr = NULL;
-                                       fib6_info_release(iter);
-                                       nsiblings--;
-                                       info->nl_net->ipv6.rt6_stats->fib_rt_entries--;
-                               } else {
-                                       ins = &iter->fib6_next;
-                               }
-                               iter = rcu_dereference_protected(*ins,
-                                       lockdep_is_held(&rt->fib6_table->tb6_lock));
+                       list_for_each_entry_safe(iter, tmp, &rt->fib6_siblings,
+                                                fib6_siblings) {
+                               iter->fib6_node = NULL;
+                               fib6_purge_rt(iter, fn, info->nl_net);
+                               if (rcu_access_pointer(fn->rr_ptr) == iter)
+                                       fn->rr_ptr = NULL;
+                               fib6_info_release(iter);
+
+                               rt->fib6_nsiblings--;
+                               info->nl_net->ipv6.rt6_stats->fib_rt_entries--;
                        }
-                       WARN_ON(nsiblings != 0);
                }
+
+               WARN_ON(rt->fib6_nsiblings != 0);
+
+               rt->fib6_node = NULL;
+               fib6_purge_rt(rt, fn, info->nl_net);
+               if (rcu_access_pointer(fn->rr_ptr) == rt)
+                       fn->rr_ptr = NULL;
+               fib6_info_release(rt);
        }
 
        return 0;
@@ -1354,8 +1346,8 @@ struct lookup_args {
        const struct in6_addr   *addr;          /* search key                   */
 };
 
-static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
-                                      struct lookup_args *args)
+static struct fib6_node *fib6_node_lookup_1(struct fib6_node *root,
+                                           struct lookup_args *args)
 {
        struct fib6_node *fn;
        __be32 dir;
@@ -1400,7 +1392,8 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
 #ifdef CONFIG_IPV6_SUBTREES
                                if (subtree) {
                                        struct fib6_node *sfn;
-                                       sfn = fib6_lookup_1(subtree, args + 1);
+                                       sfn = fib6_node_lookup_1(subtree,
+                                                                args + 1);
                                        if (!sfn)
                                                goto backtrack;
                                        fn = sfn;
@@ -1422,8 +1415,9 @@ backtrack:
 
 /* called with rcu_read_lock() held
  */
-struct fib6_node *fib6_lookup(struct fib6_node *root, const struct in6_addr *daddr,
-                             const struct in6_addr *saddr)
+struct fib6_node *fib6_node_lookup(struct fib6_node *root,
+                                  const struct in6_addr *daddr,
+                                  const struct in6_addr *saddr)
 {
        struct fib6_node *fn;
        struct lookup_args args[] = {
@@ -1442,7 +1436,7 @@ struct fib6_node *fib6_lookup(struct fib6_node *root, const struct in6_addr *dad
                }
        };
 
-       fn = fib6_lookup_1(root, daddr ? args : args + 1);
+       fn = fib6_node_lookup_1(root, daddr ? args : args + 1);
        if (!fn || fn->fn_flags & RTN_TL_ROOT)
                fn = root;
 
index bede77f..c8cf2fd 100644 (file)
@@ -71,6 +71,7 @@ struct ip6gre_net {
        struct ip6_tnl __rcu *tunnels[4][IP6_GRE_HASH_SIZE];
 
        struct ip6_tnl __rcu *collect_md_tun;
+       struct ip6_tnl __rcu *collect_md_tun_erspan;
        struct net_device *fb_tunnel_dev;
 };
 
@@ -81,6 +82,7 @@ static int ip6gre_tunnel_init(struct net_device *dev);
 static void ip6gre_tunnel_setup(struct net_device *dev);
 static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t);
 static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu);
+static void ip6erspan_tnl_link_config(struct ip6_tnl *t, int set_mtu);
 
 /* Tunnel hash table */
 
@@ -232,7 +234,12 @@ static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
        if (cand)
                return cand;
 
-       t = rcu_dereference(ign->collect_md_tun);
+       if (gre_proto == htons(ETH_P_ERSPAN) ||
+           gre_proto == htons(ETH_P_ERSPAN2))
+               t = rcu_dereference(ign->collect_md_tun_erspan);
+       else
+               t = rcu_dereference(ign->collect_md_tun);
+
        if (t && t->dev->flags & IFF_UP)
                return t;
 
@@ -261,6 +268,31 @@ static struct ip6_tnl __rcu **__ip6gre_bucket(struct ip6gre_net *ign,
        return &ign->tunnels[prio][h];
 }
 
+static void ip6gre_tunnel_link_md(struct ip6gre_net *ign, struct ip6_tnl *t)
+{
+       if (t->parms.collect_md)
+               rcu_assign_pointer(ign->collect_md_tun, t);
+}
+
+static void ip6erspan_tunnel_link_md(struct ip6gre_net *ign, struct ip6_tnl *t)
+{
+       if (t->parms.collect_md)
+               rcu_assign_pointer(ign->collect_md_tun_erspan, t);
+}
+
+static void ip6gre_tunnel_unlink_md(struct ip6gre_net *ign, struct ip6_tnl *t)
+{
+       if (t->parms.collect_md)
+               rcu_assign_pointer(ign->collect_md_tun, NULL);
+}
+
+static void ip6erspan_tunnel_unlink_md(struct ip6gre_net *ign,
+                                      struct ip6_tnl *t)
+{
+       if (t->parms.collect_md)
+               rcu_assign_pointer(ign->collect_md_tun_erspan, NULL);
+}
+
 static inline struct ip6_tnl __rcu **ip6gre_bucket(struct ip6gre_net *ign,
                const struct ip6_tnl *t)
 {
@@ -271,9 +303,6 @@ static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t)
 {
        struct ip6_tnl __rcu **tp = ip6gre_bucket(ign, t);
 
-       if (t->parms.collect_md)
-               rcu_assign_pointer(ign->collect_md_tun, t);
-
        rcu_assign_pointer(t->next, rtnl_dereference(*tp));
        rcu_assign_pointer(*tp, t);
 }
@@ -283,9 +312,6 @@ static void ip6gre_tunnel_unlink(struct ip6gre_net *ign, struct ip6_tnl *t)
        struct ip6_tnl __rcu **tp;
        struct ip6_tnl *iter;
 
-       if (t->parms.collect_md)
-               rcu_assign_pointer(ign->collect_md_tun, NULL);
-
        for (tp = ip6gre_bucket(ign, t);
             (iter = rtnl_dereference(*tp)) != NULL;
             tp = &iter->next) {
@@ -374,11 +400,23 @@ failed_free:
        return NULL;
 }
 
+static void ip6erspan_tunnel_uninit(struct net_device *dev)
+{
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
+
+       ip6erspan_tunnel_unlink_md(ign, t);
+       ip6gre_tunnel_unlink(ign, t);
+       dst_cache_reset(&t->dst_cache);
+       dev_put(dev);
+}
+
 static void ip6gre_tunnel_uninit(struct net_device *dev)
 {
        struct ip6_tnl *t = netdev_priv(dev);
        struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
 
+       ip6gre_tunnel_unlink_md(ign, t);
        ip6gre_tunnel_unlink(ign, t);
        dst_cache_reset(&t->dst_cache);
        dev_put(dev);
@@ -698,6 +736,9 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
        else
                fl6->daddr = tunnel->parms.raddr;
 
+       if (skb_cow_head(skb, dev->needed_headroom ?: tunnel->hlen))
+               return -ENOMEM;
+
        /* Push GRE header. */
        protocol = (dev->type == ARPHRD_ETHER) ? htons(ETH_P_TEB) : proto;
 
@@ -920,7 +961,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
            (ntohs(ipv6_hdr(skb)->payload_len) > skb->len - thoff))
                truncate = true;
 
-       if (skb_cow_head(skb, dev->needed_headroom))
+       if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen))
                goto tx_err;
 
        t->parms.o_flags &= ~TUNNEL_KEY;
@@ -991,11 +1032,14 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
                        erspan_build_header(skb, ntohl(t->parms.o_key),
                                            t->parms.index,
                                            truncate, false);
-               else
+               else if (t->parms.erspan_ver == 2)
                        erspan_build_header_v2(skb, ntohl(t->parms.o_key),
                                               t->parms.dir,
                                               t->parms.hwid,
                                               truncate, false);
+               else
+                       goto tx_err;
+
                fl6.daddr = t->parms.raddr;
        }
 
@@ -1031,12 +1075,11 @@ tx_err:
        return NETDEV_TX_OK;
 }
 
-static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
+static void ip6gre_tnl_link_config_common(struct ip6_tnl *t)
 {
        struct net_device *dev = t->dev;
        struct __ip6_tnl_parm *p = &t->parms;
        struct flowi6 *fl6 = &t->fl.u.ip6;
-       int t_hlen;
 
        if (dev->type != ARPHRD_ETHER) {
                memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
@@ -1063,12 +1106,13 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
                dev->flags |= IFF_POINTOPOINT;
        else
                dev->flags &= ~IFF_POINTOPOINT;
+}
 
-       t->tun_hlen = gre_calc_hlen(t->parms.o_flags);
-
-       t->hlen = t->encap_hlen + t->tun_hlen;
-
-       t_hlen = t->hlen + sizeof(struct ipv6hdr);
+static void ip6gre_tnl_link_config_route(struct ip6_tnl *t, int set_mtu,
+                                        int t_hlen)
+{
+       const struct __ip6_tnl_parm *p = &t->parms;
+       struct net_device *dev = t->dev;
 
        if (p->flags & IP6_TNL_F_CAP_XMIT) {
                int strict = (ipv6_addr_type(&p->raddr) &
@@ -1100,8 +1144,26 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
        }
 }
 
-static int ip6gre_tnl_change(struct ip6_tnl *t,
-       const struct __ip6_tnl_parm *p, int set_mtu)
+static int ip6gre_calc_hlen(struct ip6_tnl *tunnel)
+{
+       int t_hlen;
+
+       tunnel->tun_hlen = gre_calc_hlen(tunnel->parms.o_flags);
+       tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
+
+       t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
+       tunnel->dev->hard_header_len = LL_MAX_HEADER + t_hlen;
+       return t_hlen;
+}
+
+static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
+{
+       ip6gre_tnl_link_config_common(t);
+       ip6gre_tnl_link_config_route(t, set_mtu, ip6gre_calc_hlen(t));
+}
+
+static void ip6gre_tnl_copy_tnl_parm(struct ip6_tnl *t,
+                                    const struct __ip6_tnl_parm *p)
 {
        t->parms.laddr = p->laddr;
        t->parms.raddr = p->raddr;
@@ -1117,6 +1179,12 @@ static int ip6gre_tnl_change(struct ip6_tnl *t,
        t->parms.o_flags = p->o_flags;
        t->parms.fwmark = p->fwmark;
        dst_cache_reset(&t->dst_cache);
+}
+
+static int ip6gre_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p,
+                            int set_mtu)
+{
+       ip6gre_tnl_copy_tnl_parm(t, p);
        ip6gre_tnl_link_config(t, set_mtu);
        return 0;
 }
@@ -1395,11 +1463,7 @@ static int ip6gre_tunnel_init_common(struct net_device *dev)
        if (ret)
                goto cleanup_dst_cache_init;
 
-       tunnel->tun_hlen = gre_calc_hlen(tunnel->parms.o_flags);
-       tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
-       t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
-
-       dev->hard_header_len = LL_MAX_HEADER + t_hlen;
+       t_hlen = ip6gre_calc_hlen(tunnel);
        dev->mtu = ETH_DATA_LEN - t_hlen;
        if (dev->type == ARPHRD_ETHER)
                dev->mtu -= ETH_HLEN;
@@ -1749,6 +1813,19 @@ static const struct net_device_ops ip6gre_tap_netdev_ops = {
        .ndo_get_iflink = ip6_tnl_get_iflink,
 };
 
+static int ip6erspan_calc_hlen(struct ip6_tnl *tunnel)
+{
+       int t_hlen;
+
+       tunnel->tun_hlen = 8;
+       tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
+                      erspan_hdr_len(tunnel->parms.erspan_ver);
+
+       t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
+       tunnel->dev->hard_header_len = LL_MAX_HEADER + t_hlen;
+       return t_hlen;
+}
+
 static int ip6erspan_tap_init(struct net_device *dev)
 {
        struct ip6_tnl *tunnel;
@@ -1773,12 +1850,7 @@ static int ip6erspan_tap_init(struct net_device *dev)
        if (ret)
                goto cleanup_dst_cache_init;
 
-       tunnel->tun_hlen = 8;
-       tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
-                      erspan_hdr_len(tunnel->parms.erspan_ver);
-       t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
-
-       dev->hard_header_len = LL_MAX_HEADER + t_hlen;
+       t_hlen = ip6erspan_calc_hlen(tunnel);
        dev->mtu = ETH_DATA_LEN - t_hlen;
        if (dev->type == ARPHRD_ETHER)
                dev->mtu -= ETH_HLEN;
@@ -1786,7 +1858,7 @@ static int ip6erspan_tap_init(struct net_device *dev)
                dev->mtu -= 8;
 
        dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
-       ip6gre_tnl_link_config(tunnel, 1);
+       ip6erspan_tnl_link_config(tunnel, 1);
 
        return 0;
 
@@ -1800,7 +1872,7 @@ cleanup_alloc_pcpu_stats:
 
 static const struct net_device_ops ip6erspan_netdev_ops = {
        .ndo_init =             ip6erspan_tap_init,
-       .ndo_uninit =           ip6gre_tunnel_uninit,
+       .ndo_uninit =           ip6erspan_tunnel_uninit,
        .ndo_start_xmit =       ip6erspan_tunnel_xmit,
        .ndo_set_mac_address =  eth_mac_addr,
        .ndo_validate_addr =    eth_validate_addr,
@@ -1864,13 +1936,11 @@ static bool ip6gre_netlink_encap_parms(struct nlattr *data[],
        return ret;
 }
 
-static int ip6gre_newlink(struct net *src_net, struct net_device *dev,
-                         struct nlattr *tb[], struct nlattr *data[],
-                         struct netlink_ext_ack *extack)
+static int ip6gre_newlink_common(struct net *src_net, struct net_device *dev,
+                                struct nlattr *tb[], struct nlattr *data[],
+                                struct netlink_ext_ack *extack)
 {
        struct ip6_tnl *nt;
-       struct net *net = dev_net(dev);
-       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
        struct ip_tunnel_encap ipencap;
        int err;
 
@@ -1883,16 +1953,6 @@ static int ip6gre_newlink(struct net *src_net, struct net_device *dev,
                        return err;
        }
 
-       ip6gre_netlink_parms(data, &nt->parms);
-
-       if (nt->parms.collect_md) {
-               if (rtnl_dereference(ign->collect_md_tun))
-                       return -EEXIST;
-       } else {
-               if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
-                       return -EEXIST;
-       }
-
        if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS])
                eth_hw_addr_random(dev);
 
@@ -1903,51 +1963,94 @@ static int ip6gre_newlink(struct net *src_net, struct net_device *dev,
        if (err)
                goto out;
 
-       ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]);
-
        if (tb[IFLA_MTU])
                ip6_tnl_change_mtu(dev, nla_get_u32(tb[IFLA_MTU]));
 
        dev_hold(dev);
-       ip6gre_tunnel_link(ign, nt);
 
 out:
        return err;
 }
 
-static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
-                            struct nlattr *data[],
-                            struct netlink_ext_ack *extack)
+static int ip6gre_newlink(struct net *src_net, struct net_device *dev,
+                         struct nlattr *tb[], struct nlattr *data[],
+                         struct netlink_ext_ack *extack)
+{
+       struct ip6_tnl *nt = netdev_priv(dev);
+       struct net *net = dev_net(dev);
+       struct ip6gre_net *ign;
+       int err;
+
+       ip6gre_netlink_parms(data, &nt->parms);
+       ign = net_generic(net, ip6gre_net_id);
+
+       if (nt->parms.collect_md) {
+               if (rtnl_dereference(ign->collect_md_tun))
+                       return -EEXIST;
+       } else {
+               if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
+                       return -EEXIST;
+       }
+
+       err = ip6gre_newlink_common(src_net, dev, tb, data, extack);
+       if (!err) {
+               ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]);
+               ip6gre_tunnel_link_md(ign, nt);
+               ip6gre_tunnel_link(net_generic(net, ip6gre_net_id), nt);
+       }
+       return err;
+}
+
+static struct ip6_tnl *
+ip6gre_changelink_common(struct net_device *dev, struct nlattr *tb[],
+                        struct nlattr *data[], struct __ip6_tnl_parm *p_p,
+                        struct netlink_ext_ack *extack)
 {
        struct ip6_tnl *t, *nt = netdev_priv(dev);
        struct net *net = nt->net;
        struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
-       struct __ip6_tnl_parm p;
        struct ip_tunnel_encap ipencap;
 
        if (dev == ign->fb_tunnel_dev)
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
        if (ip6gre_netlink_encap_parms(data, &ipencap)) {
                int err = ip6_tnl_encap_setup(nt, &ipencap);
 
                if (err < 0)
-                       return err;
+                       return ERR_PTR(err);
        }
 
-       ip6gre_netlink_parms(data, &p);
+       ip6gre_netlink_parms(data, p_p);
 
-       t = ip6gre_tunnel_locate(net, &p, 0);
+       t = ip6gre_tunnel_locate(net, p_p, 0);
 
        if (t) {
                if (t->dev != dev)
-                       return -EEXIST;
+                       return ERR_PTR(-EEXIST);
        } else {
                t = nt;
        }
 
+       return t;
+}
+
+static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
+                            struct nlattr *data[],
+                            struct netlink_ext_ack *extack)
+{
+       struct ip6gre_net *ign = net_generic(dev_net(dev), ip6gre_net_id);
+       struct __ip6_tnl_parm p;
+       struct ip6_tnl *t;
+
+       t = ip6gre_changelink_common(dev, tb, data, &p, extack);
+       if (IS_ERR(t))
+               return PTR_ERR(t);
+
+       ip6gre_tunnel_unlink_md(ign, t);
        ip6gre_tunnel_unlink(ign, t);
        ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]);
+       ip6gre_tunnel_link_md(ign, t);
        ip6gre_tunnel_link(ign, t);
        return 0;
 }
@@ -2097,6 +2200,69 @@ static void ip6erspan_tap_setup(struct net_device *dev)
        netif_keep_dst(dev);
 }
 
+static int ip6erspan_newlink(struct net *src_net, struct net_device *dev,
+                            struct nlattr *tb[], struct nlattr *data[],
+                            struct netlink_ext_ack *extack)
+{
+       struct ip6_tnl *nt = netdev_priv(dev);
+       struct net *net = dev_net(dev);
+       struct ip6gre_net *ign;
+       int err;
+
+       ip6gre_netlink_parms(data, &nt->parms);
+       ign = net_generic(net, ip6gre_net_id);
+
+       if (nt->parms.collect_md) {
+               if (rtnl_dereference(ign->collect_md_tun_erspan))
+                       return -EEXIST;
+       } else {
+               if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
+                       return -EEXIST;
+       }
+
+       err = ip6gre_newlink_common(src_net, dev, tb, data, extack);
+       if (!err) {
+               ip6erspan_tnl_link_config(nt, !tb[IFLA_MTU]);
+               ip6erspan_tunnel_link_md(ign, nt);
+               ip6gre_tunnel_link(net_generic(net, ip6gre_net_id), nt);
+       }
+       return err;
+}
+
+static void ip6erspan_tnl_link_config(struct ip6_tnl *t, int set_mtu)
+{
+       ip6gre_tnl_link_config_common(t);
+       ip6gre_tnl_link_config_route(t, set_mtu, ip6erspan_calc_hlen(t));
+}
+
+static int ip6erspan_tnl_change(struct ip6_tnl *t,
+                               const struct __ip6_tnl_parm *p, int set_mtu)
+{
+       ip6gre_tnl_copy_tnl_parm(t, p);
+       ip6erspan_tnl_link_config(t, set_mtu);
+       return 0;
+}
+
+static int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[],
+                               struct nlattr *data[],
+                               struct netlink_ext_ack *extack)
+{
+       struct ip6gre_net *ign = net_generic(dev_net(dev), ip6gre_net_id);
+       struct __ip6_tnl_parm p;
+       struct ip6_tnl *t;
+
+       t = ip6gre_changelink_common(dev, tb, data, &p, extack);
+       if (IS_ERR(t))
+               return PTR_ERR(t);
+
+       ip6gre_tunnel_unlink_md(ign, t);
+       ip6gre_tunnel_unlink(ign, t);
+       ip6erspan_tnl_change(t, &p, !tb[IFLA_MTU]);
+       ip6erspan_tunnel_link_md(ign, t);
+       ip6gre_tunnel_link(ign, t);
+       return 0;
+}
+
 static struct rtnl_link_ops ip6gre_link_ops __read_mostly = {
        .kind           = "ip6gre",
        .maxtype        = IFLA_GRE_MAX,
@@ -2133,8 +2299,8 @@ static struct rtnl_link_ops ip6erspan_tap_ops __read_mostly = {
        .priv_size      = sizeof(struct ip6_tnl),
        .setup          = ip6erspan_tap_setup,
        .validate       = ip6erspan_tap_validate,
-       .newlink        = ip6gre_newlink,
-       .changelink     = ip6gre_changelink,
+       .newlink        = ip6erspan_newlink,
+       .changelink     = ip6erspan_changelink,
        .get_size       = ip6gre_get_size,
        .fill_info      = ip6gre_fill_info,
        .get_link_net   = ip6_tnl_get_link_net,
index 7f44930..60b0d16 100644 (file)
@@ -1494,7 +1494,8 @@ alloc_new_skb:
                if (copy > length)
                        copy = length;
 
-               if (!(rt->dst.dev->features&NETIF_F_SG)) {
+               if (!(rt->dst.dev->features&NETIF_F_SG) &&
+                   skb_tailroom(skb) >= copy) {
                        unsigned int off;
 
                        off = skb->len;
index e18b14b..0758b5b 100644 (file)
@@ -38,6 +38,7 @@
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
 MODULE_DESCRIPTION("IPv6 packet filter");
+MODULE_ALIAS("ip6t_icmp6");
 
 void *ip6t_alloc_initial_table(const struct xt_table *info)
 {
index af04167..038d661 100644 (file)
@@ -63,6 +63,7 @@
 #include <net/lwtunnel.h>
 #include <net/ip_tunnels.h>
 #include <net/l3mdev.h>
+#include <net/ip.h>
 #include <trace/events/fib6.h>
 
 #include <linux/uaccess.h>
@@ -419,11 +420,11 @@ static bool rt6_check_expired(const struct rt6_info *rt)
        return false;
 }
 
-static struct fib6_info *rt6_multipath_select(const struct net *net,
-                                             struct fib6_info *match,
-                                            struct flowi6 *fl6, int oif,
-                                            const struct sk_buff *skb,
-                                            int strict)
+struct fib6_info *fib6_multipath_select(const struct net *net,
+                                       struct fib6_info *match,
+                                       struct flowi6 *fl6, int oif,
+                                       const struct sk_buff *skb,
+                                       int strict)
 {
        struct fib6_info *sibling, *next_sibling;
 
@@ -1006,7 +1007,7 @@ static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
                pn = rcu_dereference(fn->parent);
                sn = FIB6_SUBTREE(pn);
                if (sn && sn != fn)
-                       fn = fib6_lookup(sn, NULL, saddr);
+                       fn = fib6_node_lookup(sn, NULL, saddr);
                else
                        fn = pn;
                if (fn->fn_flags & RTN_RTINFO)
@@ -1059,7 +1060,7 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
                flags &= ~RT6_LOOKUP_F_IFACE;
 
        rcu_read_lock();
-       fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
+       fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
 restart:
        f6i = rcu_dereference(fn->leaf);
        if (!f6i) {
@@ -1068,8 +1069,9 @@ restart:
                f6i = rt6_device_match(net, f6i, &fl6->saddr,
                                      fl6->flowi6_oif, flags);
                if (f6i->fib6_nsiblings && fl6->flowi6_oif == 0)
-                       f6i = rt6_multipath_select(net, f6i, fl6,
-                                                  fl6->flowi6_oif, skb, flags);
+                       f6i = fib6_multipath_select(net, f6i, fl6,
+                                                   fl6->flowi6_oif, skb,
+                                                   flags);
        }
        if (f6i == net->ipv6.fib6_null_entry) {
                fn = fib6_backtrack(fn, &fl6->saddr);
@@ -1077,6 +1079,8 @@ restart:
                        goto restart;
        }
 
+       trace_fib6_table_lookup(net, f6i, table, fl6);
+
        /* Search through exception table */
        rt = rt6_find_cached_rt(f6i, &fl6->daddr, &fl6->saddr);
        if (rt) {
@@ -1095,8 +1099,6 @@ restart:
 
        rcu_read_unlock();
 
-       trace_fib6_table_lookup(net, rt, table, fl6);
-
        return rt;
 }
 
@@ -1799,23 +1801,14 @@ void rt6_age_exceptions(struct fib6_info *rt,
        rcu_read_unlock_bh();
 }
 
-struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
-                              int oif, struct flowi6 *fl6,
-                              const struct sk_buff *skb, int flags)
+/* must be called with rcu lock held */
+struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table,
+                                   int oif, struct flowi6 *fl6, int strict)
 {
        struct fib6_node *fn, *saved_fn;
        struct fib6_info *f6i;
-       struct rt6_info *rt;
-       int strict = 0;
-
-       strict |= flags & RT6_LOOKUP_F_IFACE;
-       strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
-       if (net->ipv6.devconf_all->forwarding == 0)
-               strict |= RT6_LOOKUP_F_REACHABLE;
-
-       rcu_read_lock();
 
-       fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
+       fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
        saved_fn = fn;
 
        if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF)
@@ -1823,8 +1816,6 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
 
 redo_rt6_select:
        f6i = rt6_select(net, fn, oif, strict);
-       if (f6i->fib6_nsiblings)
-               f6i = rt6_multipath_select(net, f6i, fl6, oif, skb, strict);
        if (f6i == net->ipv6.fib6_null_entry) {
                fn = fib6_backtrack(fn, &fl6->saddr);
                if (fn)
@@ -1837,11 +1828,34 @@ redo_rt6_select:
                }
        }
 
+       trace_fib6_table_lookup(net, f6i, table, fl6);
+
+       return f6i;
+}
+
+struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
+                              int oif, struct flowi6 *fl6,
+                              const struct sk_buff *skb, int flags)
+{
+       struct fib6_info *f6i;
+       struct rt6_info *rt;
+       int strict = 0;
+
+       strict |= flags & RT6_LOOKUP_F_IFACE;
+       strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE;
+       if (net->ipv6.devconf_all->forwarding == 0)
+               strict |= RT6_LOOKUP_F_REACHABLE;
+
+       rcu_read_lock();
+
+       f6i = fib6_table_lookup(net, table, oif, fl6, strict);
+       if (f6i->fib6_nsiblings)
+               f6i = fib6_multipath_select(net, f6i, fl6, oif, skb, strict);
+
        if (f6i == net->ipv6.fib6_null_entry) {
                rt = net->ipv6.ip6_null_entry;
                rcu_read_unlock();
                dst_hold(&rt->dst);
-               trace_fib6_table_lookup(net, rt, table, fl6);
                return rt;
        }
 
@@ -1852,7 +1866,6 @@ redo_rt6_select:
                        dst_use_noref(&rt->dst, jiffies);
 
                rcu_read_unlock();
-               trace_fib6_table_lookup(net, rt, table, fl6);
                return rt;
        } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
                            !(f6i->fib6_flags & RTF_GATEWAY))) {
@@ -1878,9 +1891,7 @@ redo_rt6_select:
                        dst_hold(&uncached_rt->dst);
                }
 
-               trace_fib6_table_lookup(net, uncached_rt, table, fl6);
                return uncached_rt;
-
        } else {
                /* Get a percpu copy */
 
@@ -1894,7 +1905,7 @@ redo_rt6_select:
 
                local_bh_enable();
                rcu_read_unlock();
-               trace_fib6_table_lookup(net, pcpu_rt, table, fl6);
+
                return pcpu_rt;
        }
 }
@@ -2425,7 +2436,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
         */
 
        rcu_read_lock();
-       fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
+       fn = fib6_node_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
 restart:
        for_each_fib6_node_rt_rcu(fn) {
                if (rt->fib6_nh.nh_flags & RTNH_F_DEAD)
@@ -2479,7 +2490,7 @@ out:
 
        rcu_read_unlock();
 
-       trace_fib6_table_lookup(net, ret, table, fl6);
+       trace_fib6_table_lookup(net, rt, table, fl6);
        return ret;
 };
 
@@ -3781,7 +3792,7 @@ static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt)
                        lockdep_is_held(&rt->fib6_table->tb6_lock));
        while (iter) {
                if (iter->fib6_metric == rt->fib6_metric &&
-                   rt6_qualify_for_ecmp(iter))
+                   iter->fib6_nsiblings)
                        return iter;
                iter = rcu_dereference_protected(iter->fib6_next,
                                lockdep_is_held(&rt->fib6_table->tb6_lock));
@@ -4073,6 +4084,9 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
        [RTA_UID]               = { .type = NLA_U32 },
        [RTA_MARK]              = { .type = NLA_U32 },
        [RTA_TABLE]             = { .type = NLA_U32 },
+       [RTA_IP_PROTO]          = { .type = NLA_U8 },
+       [RTA_SPORT]             = { .type = NLA_U16 },
+       [RTA_DPORT]             = { .type = NLA_U16 },
 };
 
 static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -4371,6 +4385,7 @@ static int ip6_route_multipath_add(struct fib6_config *cfg,
                 */
                cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL |
                                                     NLM_F_REPLACE);
+               cfg->fc_nlinfo.nlh->nlmsg_flags |= NLM_F_APPEND;
                nhn++;
        }
 
@@ -4784,6 +4799,19 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
        else
                fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
 
+       if (tb[RTA_SPORT])
+               fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);
+
+       if (tb[RTA_DPORT])
+               fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);
+
+       if (tb[RTA_IP_PROTO]) {
+               err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
+                                                 &fl6.flowi6_proto, extack);
+               if (err)
+                       goto errout;
+       }
+
        if (iif) {
                struct net_device *dev;
                int flags = 0;
index 2839c1b..426c9d2 100644 (file)
@@ -1053,7 +1053,8 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6,
                        return -EINVAL;
                if (udp_sk(sk)->no_check6_tx)
                        return -EINVAL;
-               if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite)
+               if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite ||
+                   dst_xfrm(skb_dst(skb)))
                        return -EIO;
 
                skb_shinfo(skb)->gso_size = cork->gso_size;
index 85dbaa8..bdf6fa7 100644 (file)
@@ -695,7 +695,7 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
        if (sta) {
                ret = 0;
                memcpy(mac, sta->sta.addr, ETH_ALEN);
-               sta_set_sinfo(sta, sinfo);
+               sta_set_sinfo(sta, sinfo, true);
        }
 
        mutex_unlock(&local->sta_mtx);
@@ -724,7 +724,7 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
        sta = sta_info_get_bss(sdata, mac);
        if (sta) {
                ret = 0;
-               sta_set_sinfo(sta, sinfo);
+               sta_set_sinfo(sta, sinfo, true);
        }
 
        mutex_unlock(&local->sta_mtx);
@@ -2376,6 +2376,11 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
            (WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
 
+       if (changed & (WIPHY_PARAM_TXQ_LIMIT |
+                      WIPHY_PARAM_TXQ_MEMORY_LIMIT |
+                      WIPHY_PARAM_TXQ_QUANTUM))
+               ieee80211_txq_set_params(local);
+
        return 0;
 }
 
@@ -3705,6 +3710,99 @@ static int ieee80211_set_multicast_to_unicast(struct wiphy *wiphy,
        return 0;
 }
 
+void ieee80211_fill_txq_stats(struct cfg80211_txq_stats *txqstats,
+                             struct txq_info *txqi)
+{
+       if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_BACKLOG_BYTES))) {
+               txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_BYTES);
+               txqstats->backlog_bytes = txqi->tin.backlog_bytes;
+       }
+
+       if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS))) {
+               txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS);
+               txqstats->backlog_packets = txqi->tin.backlog_packets;
+       }
+
+       if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_FLOWS))) {
+               txqstats->filled |= BIT(NL80211_TXQ_STATS_FLOWS);
+               txqstats->flows = txqi->tin.flows;
+       }
+
+       if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_DROPS))) {
+               txqstats->filled |= BIT(NL80211_TXQ_STATS_DROPS);
+               txqstats->drops = txqi->cstats.drop_count;
+       }
+
+       if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_ECN_MARKS))) {
+               txqstats->filled |= BIT(NL80211_TXQ_STATS_ECN_MARKS);
+               txqstats->ecn_marks = txqi->cstats.ecn_mark;
+       }
+
+       if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_OVERLIMIT))) {
+               txqstats->filled |= BIT(NL80211_TXQ_STATS_OVERLIMIT);
+               txqstats->overlimit = txqi->tin.overlimit;
+       }
+
+       if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_COLLISIONS))) {
+               txqstats->filled |= BIT(NL80211_TXQ_STATS_COLLISIONS);
+               txqstats->collisions = txqi->tin.collisions;
+       }
+
+       if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_TX_BYTES))) {
+               txqstats->filled |= BIT(NL80211_TXQ_STATS_TX_BYTES);
+               txqstats->tx_bytes = txqi->tin.tx_bytes;
+       }
+
+       if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_TX_PACKETS))) {
+               txqstats->filled |= BIT(NL80211_TXQ_STATS_TX_PACKETS);
+               txqstats->tx_packets = txqi->tin.tx_packets;
+       }
+}
+
+static int ieee80211_get_txq_stats(struct wiphy *wiphy,
+                                  struct wireless_dev *wdev,
+                                  struct cfg80211_txq_stats *txqstats)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata;
+       int ret = 0;
+
+       if (!local->ops->wake_tx_queue)
+               return 1;
+
+       spin_lock_bh(&local->fq.lock);
+       rcu_read_lock();
+
+       if (wdev) {
+               sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+               if (!sdata->vif.txq) {
+                       ret = 1;
+                       goto out;
+               }
+               ieee80211_fill_txq_stats(txqstats, to_txq_info(sdata->vif.txq));
+       } else {
+               /* phy stats */
+               txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS) |
+                                   BIT(NL80211_TXQ_STATS_BACKLOG_BYTES) |
+                                   BIT(NL80211_TXQ_STATS_OVERLIMIT) |
+                                   BIT(NL80211_TXQ_STATS_OVERMEMORY) |
+                                   BIT(NL80211_TXQ_STATS_COLLISIONS) |
+                                   BIT(NL80211_TXQ_STATS_MAX_FLOWS);
+               txqstats->backlog_packets = local->fq.backlog;
+               txqstats->backlog_bytes = local->fq.memory_usage;
+               txqstats->overlimit = local->fq.overlimit;
+               txqstats->overmemory = local->fq.overmemory;
+               txqstats->collisions = local->fq.collisions;
+               txqstats->max_flows = local->fq.flows_cnt;
+       }
+
+out:
+       rcu_read_unlock();
+       spin_unlock_bh(&local->fq.lock);
+
+       return ret;
+}
+
 const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -3798,4 +3896,5 @@ const struct cfg80211_ops mac80211_config_ops = {
        .del_nan_func = ieee80211_del_nan_func,
        .set_multicast_to_unicast = ieee80211_set_multicast_to_unicast,
        .tx_control_port = ieee80211_tx_control_port,
+       .get_txq_stats = ieee80211_get_txq_stats,
 };
index 4d82fe7..8f69980 100644 (file)
@@ -2,6 +2,7 @@
 /*
 * Portions of this file
 * Copyright(c) 2016 Intel Deutschland GmbH
+* Copyright (C) 2018 Intel Corporation
 */
 
 #ifndef __MAC80211_DRIVER_OPS
@@ -813,7 +814,8 @@ drv_allow_buffered_frames(struct ieee80211_local *local,
 }
 
 static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
-                                     struct ieee80211_sub_if_data *sdata)
+                                     struct ieee80211_sub_if_data *sdata,
+                                     u16 duration)
 {
        might_sleep();
 
@@ -821,9 +823,9 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
                return;
        WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
 
-       trace_drv_mgd_prepare_tx(local, sdata);
+       trace_drv_mgd_prepare_tx(local, sdata, duration);
        if (local->ops->mgd_prepare_tx)
-               local->ops->mgd_prepare_tx(&local->hw, &sdata->vif);
+               local->ops->mgd_prepare_tx(&local->hw, &sdata->vif, duration);
        trace_drv_return_void(local);
 }
 
index 9cc986d..690c142 100644 (file)
@@ -4,6 +4,7 @@
  * Copied from cfg.c - originally
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2014      Intel Corporation (Author: Johannes Berg)
+ * Copyright (C) 2018 Intel Corporation
  *
  * This file is GPLv2 as found in COPYING.
  */
@@ -106,8 +107,8 @@ static void ieee80211_get_stats(struct net_device *dev,
                if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
                        goto do_survey;
 
-               sinfo.filled = 0;
-               sta_set_sinfo(sta, &sinfo);
+               memset(&sinfo, 0, sizeof(sinfo));
+               sta_set_sinfo(sta, &sinfo, false);
 
                i = 0;
                ADD_STA_STATS(sta);
@@ -116,11 +117,11 @@ static void ieee80211_get_stats(struct net_device *dev,
 
 
                if (sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE))
-                       data[i] = 100000 *
+                       data[i] = 100000ULL *
                                cfg80211_calculate_bitrate(&sinfo.txrate);
                i++;
                if (sinfo.filled & BIT(NL80211_STA_INFO_RX_BITRATE))
-                       data[i] = 100000 *
+                       data[i] = 100000ULL *
                                cfg80211_calculate_bitrate(&sinfo.rxrate);
                i++;
 
@@ -133,8 +134,8 @@ static void ieee80211_get_stats(struct net_device *dev,
                        if (sta->sdata->dev != dev)
                                continue;
 
-                       sinfo.filled = 0;
-                       sta_set_sinfo(sta, &sinfo);
+                       memset(&sinfo, 0, sizeof(sinfo));
+                       sta_set_sinfo(sta, &sinfo, false);
                        i = 0;
                        ADD_STA_STATS(sta);
                }
index c78036a..26a7ba3 100644 (file)
@@ -301,26 +301,27 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
                ___ieee80211_stop_tx_ba_session(sta, i, reason);
        mutex_unlock(&sta->ampdu_mlme.mtx);
 
-       /* stopping might queue the work again - so cancel only afterwards */
-       cancel_work_sync(&sta->ampdu_mlme.work);
-
        /*
         * In case the tear down is part of a reconfigure due to HW restart
         * request, it is possible that the low level driver requested to stop
         * the BA session, so handle it to properly clean tid_tx data.
         */
-       mutex_lock(&sta->ampdu_mlme.mtx);
-       for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
-               struct tid_ampdu_tx *tid_tx =
-                       rcu_dereference_protected_tid_tx(sta, i);
+       if(reason == AGG_STOP_DESTROY_STA) {
+               cancel_work_sync(&sta->ampdu_mlme.work);
 
-               if (!tid_tx)
-                       continue;
+               mutex_lock(&sta->ampdu_mlme.mtx);
+               for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
+                       struct tid_ampdu_tx *tid_tx =
+                               rcu_dereference_protected_tid_tx(sta, i);
 
-               if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state))
-                       ieee80211_stop_tx_ba_cb(sta, i, tid_tx);
+                       if (!tid_tx)
+                               continue;
+
+                       if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state))
+                               ieee80211_stop_tx_ba_cb(sta, i, tid_tx);
+               }
+               mutex_unlock(&sta->ampdu_mlme.mtx);
        }
-       mutex_unlock(&sta->ampdu_mlme.mtx);
 }
 
 void ieee80211_ba_session_work(struct work_struct *work)
@@ -328,16 +329,11 @@ void ieee80211_ba_session_work(struct work_struct *work)
        struct sta_info *sta =
                container_of(work, struct sta_info, ampdu_mlme.work);
        struct tid_ampdu_tx *tid_tx;
+       bool blocked;
        int tid;
 
-       /*
-        * When this flag is set, new sessions should be
-        * blocked, and existing sessions will be torn
-        * down by the code that set the flag, so this
-        * need not run.
-        */
-       if (test_sta_flag(sta, WLAN_STA_BLOCK_BA))
-               return;
+       /* When this flag is set, new sessions should be blocked. */
+       blocked = test_sta_flag(sta, WLAN_STA_BLOCK_BA);
 
        mutex_lock(&sta->ampdu_mlme.mtx);
        for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
@@ -352,7 +348,8 @@ void ieee80211_ba_session_work(struct work_struct *work)
                                sta, tid, WLAN_BACK_RECIPIENT,
                                WLAN_REASON_UNSPECIFIED, true);
 
-               if (test_and_clear_bit(tid,
+               if (!blocked &&
+                   test_and_clear_bit(tid,
                                       sta->ampdu_mlme.tid_rx_manage_offl))
                        ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid,
                                                         IEEE80211_MAX_AMPDU_BUF,
@@ -367,7 +364,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
                spin_lock_bh(&sta->lock);
 
                tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
-               if (tid_tx) {
+               if (!blocked && tid_tx) {
                        /*
                         * Assign it over to the normal tid_tx array
                         * where it "goes live".
@@ -390,7 +387,8 @@ void ieee80211_ba_session_work(struct work_struct *work)
                if (!tid_tx)
                        continue;
 
-               if (test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state))
+               if (!blocked &&
+                   test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state))
                        ieee80211_start_tx_ba_cb(sta, tid, tid_tx);
                if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state))
                        ___ieee80211_stop_tx_ba_session(sta, tid,
index 6372dbd..d1978aa 100644 (file)
@@ -2012,6 +2012,7 @@ static inline bool ieee80211_can_run_worker(struct ieee80211_local *local)
 }
 
 int ieee80211_txq_setup_flows(struct ieee80211_local *local);
+void ieee80211_txq_set_params(struct ieee80211_local *local);
 void ieee80211_txq_teardown_flows(struct ieee80211_local *local);
 void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
                        struct sta_info *sta,
@@ -2020,6 +2021,8 @@ void ieee80211_txq_purge(struct ieee80211_local *local,
                         struct txq_info *txqi);
 void ieee80211_txq_remove_vlan(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata);
+void ieee80211_fill_txq_stats(struct cfg80211_txq_stats *txqstats,
+                             struct txq_info *txqi);
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
                         const u8 *extra, size_t extra_len, const u8 *bssid,
index 9ea17af..4d2e797 100644 (file)
@@ -565,6 +565,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
        if (!ops->set_key)
                wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
 
+       if (ops->wake_tx_queue)
+               wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS);
+
        wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM);
 
        wiphy->bss_priv_size = sizeof(struct ieee80211_bss);
index 2330687..a59187c 100644 (file)
@@ -864,7 +864,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                return;
        }
 
-       drv_mgd_prepare_tx(local, sdata);
+       drv_mgd_prepare_tx(local, sdata, 0);
 
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
        if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
@@ -2022,7 +2022,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
                 */
                if (ieee80211_hw_check(&local->hw, DEAUTH_NEED_MGD_TX_PREP) &&
                    !ifmgd->have_beacon)
-                       drv_mgd_prepare_tx(sdata->local, sdata);
+                       drv_mgd_prepare_tx(sdata->local, sdata, 0);
 
                ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype,
                                               reason, tx, frame_buf);
@@ -2560,7 +2560,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
        if (!elems.challenge)
                return;
        auth_data->expected_transaction = 4;
-       drv_mgd_prepare_tx(sdata->local, sdata);
+       drv_mgd_prepare_tx(sdata->local, sdata, 0);
        if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
                tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
                           IEEE80211_TX_INTFL_MLME_CONN_TX;
@@ -3769,6 +3769,7 @@ static int ieee80211_auth(struct ieee80211_sub_if_data *sdata)
        u32 tx_flags = 0;
        u16 trans = 1;
        u16 status = 0;
+       u16 prepare_tx_duration = 0;
 
        sdata_assert_lock(sdata);
 
@@ -3790,7 +3791,11 @@ static int ieee80211_auth(struct ieee80211_sub_if_data *sdata)
                return -ETIMEDOUT;
        }
 
-       drv_mgd_prepare_tx(local, sdata);
+       if (auth_data->algorithm == WLAN_AUTH_SAE)
+               prepare_tx_duration =
+                       jiffies_to_msecs(IEEE80211_AUTH_TIMEOUT_SAE);
+
+       drv_mgd_prepare_tx(local, sdata, prepare_tx_duration);
 
        sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
                   auth_data->bss->bssid, auth_data->tries,
@@ -4994,7 +4999,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                           req->bssid, req->reason_code,
                           ieee80211_get_reason_code_string(req->reason_code));
 
-               drv_mgd_prepare_tx(sdata->local, sdata);
+               drv_mgd_prepare_tx(sdata->local, sdata, 0);
                ieee80211_send_deauth_disassoc(sdata, req->bssid,
                                               IEEE80211_STYPE_DEAUTH,
                                               req->reason_code, tx,
@@ -5014,7 +5019,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                           req->bssid, req->reason_code,
                           ieee80211_get_reason_code_string(req->reason_code));
 
-               drv_mgd_prepare_tx(sdata->local, sdata);
+               drv_mgd_prepare_tx(sdata->local, sdata, 0);
                ieee80211_send_deauth_disassoc(sdata, req->bssid,
                                               IEEE80211_STYPE_DEAUTH,
                                               req->reason_code, tx,
index 03102af..0a38cc1 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -97,27 +98,27 @@ static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
  */
 static void remove_monitor_info(struct sk_buff *skb,
                                unsigned int present_fcs_len,
-                               unsigned int rtap_vendor_space)
+                               unsigned int rtap_space)
 {
        if (present_fcs_len)
                __pskb_trim(skb, skb->len - present_fcs_len);
-       __pskb_pull(skb, rtap_vendor_space);
+       __pskb_pull(skb, rtap_space);
 }
 
 static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
-                                    unsigned int rtap_vendor_space)
+                                    unsigned int rtap_space)
 {
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr;
 
-       hdr = (void *)(skb->data + rtap_vendor_space);
+       hdr = (void *)(skb->data + rtap_space);
 
        if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
                            RX_FLAG_FAILED_PLCP_CRC |
                            RX_FLAG_ONLY_MONITOR))
                return true;
 
-       if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
+       if (unlikely(skb->len < 16 + present_fcs_len + rtap_space))
                return true;
 
        if (ieee80211_is_ctl(hdr->frame_control) &&
@@ -199,7 +200,7 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
 
 static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
                                         struct sk_buff *skb,
-                                        int rtap_vendor_space)
+                                        int rtap_space)
 {
        struct {
                struct ieee80211_hdr_3addr hdr;
@@ -212,14 +213,14 @@ static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
 
        BUILD_BUG_ON(sizeof(action) != IEEE80211_MIN_ACTION_SIZE + 1);
 
-       if (skb->len < rtap_vendor_space + sizeof(action) +
+       if (skb->len < rtap_space + sizeof(action) +
                       VHT_MUMIMO_GROUPS_DATA_LEN)
                return;
 
        if (!is_valid_ether_addr(sdata->u.mntr.mu_follow_addr))
                return;
 
-       skb_copy_bits(skb, rtap_vendor_space, &action, sizeof(action));
+       skb_copy_bits(skb, rtap_space, &action, sizeof(action));
 
        if (!ieee80211_is_action(action.hdr.frame_control))
                return;
@@ -545,7 +546,7 @@ static struct sk_buff *
 ieee80211_make_monitor_skb(struct ieee80211_local *local,
                           struct sk_buff **origskb,
                           struct ieee80211_rate *rate,
-                          int rtap_vendor_space, bool use_origskb)
+                          int rtap_space, bool use_origskb)
 {
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(*origskb);
        int rt_hdrlen, needed_headroom;
@@ -553,7 +554,7 @@ ieee80211_make_monitor_skb(struct ieee80211_local *local,
 
        /* room for the radiotap header based on driver features */
        rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, *origskb);
-       needed_headroom = rt_hdrlen - rtap_vendor_space;
+       needed_headroom = rt_hdrlen - rtap_space;
 
        if (use_origskb) {
                /* only need to expand headroom if necessary */
@@ -607,7 +608,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
        struct ieee80211_sub_if_data *sdata;
        struct sk_buff *monskb = NULL;
        int present_fcs_len = 0;
-       unsigned int rtap_vendor_space = 0;
+       unsigned int rtap_space = 0;
        struct ieee80211_sub_if_data *monitor_sdata =
                rcu_dereference(local->monitor_sdata);
        bool only_monitor = false;
@@ -615,7 +616,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
        if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
                struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
 
-               rtap_vendor_space = sizeof(*rtap) + rtap->len + rtap->pad;
+               rtap_space += sizeof(*rtap) + rtap->len + rtap->pad;
        }
 
        /*
@@ -638,13 +639,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
        }
 
        /* ensure hdr->frame_control and vendor radiotap data are in skb head */
-       if (!pskb_may_pull(origskb, 2 + rtap_vendor_space)) {
+       if (!pskb_may_pull(origskb, 2 + rtap_space)) {
                dev_kfree_skb(origskb);
                return NULL;
        }
 
-       only_monitor = should_drop_frame(origskb, present_fcs_len,
-                                        rtap_vendor_space);
+       only_monitor = should_drop_frame(origskb, present_fcs_len, rtap_space);
 
        if (!local->monitors || (status->flag & RX_FLAG_SKIP_MONITOR)) {
                if (only_monitor) {
@@ -652,12 +652,11 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
                        return NULL;
                }
 
-               remove_monitor_info(origskb, present_fcs_len,
-                                   rtap_vendor_space);
+               remove_monitor_info(origskb, present_fcs_len, rtap_space);
                return origskb;
        }
 
-       ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_vendor_space);
+       ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space);
 
        list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
                bool last_monitor = list_is_last(&sdata->u.mntr.list,
@@ -665,8 +664,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
 
                if (!monskb)
                        monskb = ieee80211_make_monitor_skb(local, &origskb,
-                                                           rate,
-                                                           rtap_vendor_space,
+                                                           rate, rtap_space,
                                                            only_monitor &&
                                                            last_monitor);
 
@@ -698,7 +696,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
        if (!origskb)
                return NULL;
 
-       remove_monitor_info(origskb, present_fcs_len, rtap_vendor_space);
+       remove_monitor_info(origskb, present_fcs_len, rtap_space);
        return origskb;
 }
 
index 655c3d8..6428f1a 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -357,6 +358,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        sta->last_connected = ktime_get_seconds();
        ewma_signal_init(&sta->rx_stats_avg.signal);
+       ewma_avg_signal_init(&sta->status_stats.avg_ack_signal);
        for (i = 0; i < ARRAY_SIZE(sta->rx_stats_avg.chain_signal); i++)
                ewma_signal_init(&sta->rx_stats_avg.chain_signal[i]);
 
@@ -1006,7 +1008,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
 
        sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
        if (sinfo)
-               sta_set_sinfo(sta, sinfo);
+               sta_set_sinfo(sta, sinfo, true);
        cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
        kfree(sinfo);
 
@@ -1992,7 +1994,6 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate,
                int band = STA_STATS_GET(LEGACY_BAND, rate);
                int rate_idx = STA_STATS_GET(LEGACY_IDX, rate);
 
-               rinfo->flags = 0;
                sband = local->hw.wiphy->bands[band];
                brate = sband->bitrates[rate_idx].bitrate;
                if (rinfo->bw == RATE_INFO_BW_5)
@@ -2051,6 +2052,18 @@ static void sta_set_tidstats(struct sta_info *sta,
                tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU_FAILED);
                tidstats->tx_msdu_failed = sta->status_stats.msdu_failed[tid];
        }
+
+       if (local->ops->wake_tx_queue && tid < IEEE80211_NUM_TIDS) {
+               spin_lock_bh(&local->fq.lock);
+               rcu_read_lock();
+
+               tidstats->filled |= BIT(NL80211_TID_STATS_TXQ_STATS);
+               ieee80211_fill_txq_stats(&tidstats->txq_stats,
+                                        to_txq_info(sta->sta.txq[tid]));
+
+               rcu_read_unlock();
+               spin_unlock_bh(&local->fq.lock);
+       }
 }
 
 static inline u64 sta_get_stats_bytes(struct ieee80211_sta_rx_stats *rxstats)
@@ -2066,7 +2079,8 @@ static inline u64 sta_get_stats_bytes(struct ieee80211_sta_rx_stats *rxstats)
        return value;
 }
 
-void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
+void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
+                  bool tidstats)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
@@ -2220,11 +2234,12 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                        sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
        }
 
-       sinfo->filled |= BIT(NL80211_STA_INFO_TID_STATS);
-       for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
-               struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i];
+       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);
+                       sta_set_tidstats(sta, tidstats, i);
+               }
        }
 
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
@@ -2294,6 +2309,15 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                sinfo->ack_signal = sta->status_stats.last_ack_signal;
                sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL);
        }
+
+       if (ieee80211_hw_check(&sta->local->hw, REPORTS_TX_ACK_STATUS) &&
+           !(sinfo->filled & BIT_ULL(NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG))) {
+               sinfo->avg_ack_signal =
+                       -(s8)ewma_avg_signal_read(
+                               &sta->status_stats.avg_ack_signal);
+               sinfo->filled |=
+                       BIT_ULL(NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG);
+       }
 }
 
 u32 sta_get_expected_throughput(struct sta_info *sta)
index f64eb86..81b35f6 100644 (file)
@@ -119,6 +119,7 @@ enum ieee80211_sta_info_flags {
 #define HT_AGG_STATE_START_CB          6
 #define HT_AGG_STATE_STOP_CB           7
 
+DECLARE_EWMA(avg_signal, 10, 8)
 enum ieee80211_agg_stop_reason {
        AGG_STOP_DECLINED,
        AGG_STOP_LOCAL_REQUEST,
@@ -550,6 +551,7 @@ struct sta_info {
                unsigned long last_ack;
                s8 last_ack_signal;
                bool ack_signal_filled;
+               struct ewma_avg_signal avg_ack_signal;
        } status_stats;
 
        /* Updated from TX path only, no locking requirements */
@@ -742,7 +744,8 @@ static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata)
 void sta_set_rate_info_tx(struct sta_info *sta,
                          const struct ieee80211_tx_rate *rate,
                          struct rate_info *rinfo);
-void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo);
+void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
+                  bool tidstats);
 
 u32 sta_get_expected_throughput(struct sta_info *sta);
 
index 743e89c..9a6d720 100644 (file)
@@ -195,6 +195,8 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
                        sta->status_stats.last_ack_signal =
                                         (s8)txinfo->status.ack_signal;
                        sta->status_stats.ack_signal_filled = true;
+                       ewma_avg_signal_add(&sta->status_stats.avg_ack_signal,
+                                           -txinfo->status.ack_signal);
                }
        }
 
index 591ad02..80a7edf 100644 (file)
@@ -2,6 +2,7 @@
 /*
 * Portions of this file
 * Copyright(c) 2016 Intel Deutschland GmbH
+* Copyright (C) 2018 Intel Corporation
 */
 
 #if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ)
@@ -1413,11 +1414,29 @@ DEFINE_EVENT(release_evt, drv_allow_buffered_frames,
        TP_ARGS(local, sta, tids, num_frames, reason, more_data)
 );
 
-DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx,
+TRACE_EVENT(drv_mgd_prepare_tx,
        TP_PROTO(struct ieee80211_local *local,
-                struct ieee80211_sub_if_data *sdata),
+                struct ieee80211_sub_if_data *sdata,
+                u16 duration),
 
-       TP_ARGS(local, sdata)
+       TP_ARGS(local, sdata, duration),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               __field(u32, duration)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               __entry->duration = duration;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT VIF_PR_FMT " duration: %u",
+               LOCAL_PR_ARG, VIF_PR_ARG, __entry->duration
+       )
 );
 
 DEFINE_EVENT(local_sdata_evt, drv_mgd_protect_tdls_discover,
index 05a265c..44b5dfe 100644 (file)
@@ -1460,6 +1460,24 @@ void ieee80211_txq_purge(struct ieee80211_local *local,
        ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
 }
 
+void ieee80211_txq_set_params(struct ieee80211_local *local)
+{
+       if (local->hw.wiphy->txq_limit)
+               local->fq.limit = local->hw.wiphy->txq_limit;
+       else
+               local->hw.wiphy->txq_limit = local->fq.limit;
+
+       if (local->hw.wiphy->txq_memory_limit)
+               local->fq.memory_limit = local->hw.wiphy->txq_memory_limit;
+       else
+               local->hw.wiphy->txq_memory_limit = local->fq.memory_limit;
+
+       if (local->hw.wiphy->txq_quantum)
+               local->fq.quantum = local->hw.wiphy->txq_quantum;
+       else
+               local->hw.wiphy->txq_quantum = local->fq.quantum;
+}
+
 int ieee80211_txq_setup_flows(struct ieee80211_local *local)
 {
        struct fq *fq = &local->fq;
@@ -1509,6 +1527,8 @@ int ieee80211_txq_setup_flows(struct ieee80211_local *local)
        for (i = 0; i < fq->flows_cnt; i++)
                codel_vars_init(&local->cvars[i]);
 
+       ieee80211_txq_set_params(local);
+
        return 0;
 }
 
@@ -4085,6 +4105,31 @@ unlock:
 }
 EXPORT_SYMBOL(ieee80211_csa_update_counter);
 
+void ieee80211_csa_set_counter(struct ieee80211_vif *vif, u8 counter)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct beacon_data *beacon = NULL;
+
+       rcu_read_lock();
+
+       if (sdata->vif.type == NL80211_IFTYPE_AP)
+               beacon = rcu_dereference(sdata->u.ap.beacon);
+       else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+               beacon = rcu_dereference(sdata->u.ibss.presp);
+       else if (ieee80211_vif_is_mesh(&sdata->vif))
+               beacon = rcu_dereference(sdata->u.mesh.beacon);
+
+       if (!beacon)
+               goto unlock;
+
+       if (counter < beacon->csa_current_counter)
+               beacon->csa_current_counter = counter;
+
+unlock:
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee80211_csa_set_counter);
+
 bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
index 11f9cfc..2d82c88 100644 (file)
@@ -2793,12 +2793,13 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
 
        memset(&ri, 0, sizeof(ri));
 
+       ri.bw = status->bw;
+
        /* Fill cfg80211 rate info */
        switch (status->encoding) {
        case RX_ENC_HT:
                ri.mcs = status->rate_idx;
                ri.flags |= RATE_INFO_FLAGS_MCS;
-               ri.bw = status->bw;
                if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
                        ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
                break;
@@ -2806,7 +2807,6 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
                ri.flags |= RATE_INFO_FLAGS_VHT_MCS;
                ri.mcs = status->rate_idx;
                ri.nss = status->nss;
-               ri.bw = status->bw;
                if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
                        ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
                break;
@@ -2818,8 +2818,6 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
                int shift = 0;
                int bitrate;
 
-               ri.bw = status->bw;
-
                switch (status->bw) {
                case RATE_INFO_BW_10:
                        shift = 1;
index ce94979..a6b7c7d 100644 (file)
@@ -347,7 +347,7 @@ static int ncsi_rsp_handler_svf(struct ncsi_request *nr)
 
        cmd = (struct ncsi_cmd_svf_pkt *)skb_network_header(nr->cmd);
        ncf = &nc->vlan_filter;
-       if (cmd->index > ncf->n_vids)
+       if (cmd->index == 0 || cmd->index > ncf->n_vids)
                return -ERANGE;
 
        /* Add or remove the VLAN filter. Remember HW indexes from 1 */
@@ -445,7 +445,8 @@ static int ncsi_rsp_handler_sma(struct ncsi_request *nr)
        ncf = &nc->mac_filter;
        bitmap = &ncf->bitmap;
 
-       if (cmd->index > ncf->n_uc + ncf->n_mc + ncf->n_mixed)
+       if (cmd->index == 0 ||
+           cmd->index > ncf->n_uc + ncf->n_mc + ncf->n_mixed)
                return -ERANGE;
 
        index = (cmd->index - 1) * ETH_ALEN;
index e0ae4aa..168af54 100644 (file)
@@ -611,7 +611,8 @@ const struct nf_conntrack_zone nf_ct_zone_dflt = {
 EXPORT_SYMBOL_GPL(nf_ct_zone_dflt);
 #endif /* CONFIG_NF_CONNTRACK */
 
-static void __net_init __netfilter_net_init(struct nf_hook_entries **e, int max)
+static void __net_init
+__netfilter_net_init(struct nf_hook_entries __rcu **e, int max)
 {
        int h;
 
index 370abbf..75de465 100644 (file)
@@ -232,7 +232,10 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
 static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
 {
        unsigned int hash;
-       bool ret;
+       bool ret = false;
+
+       if (cp->flags & IP_VS_CONN_F_ONE_PACKET)
+               return refcount_dec_if_one(&cp->refcnt);
 
        hash = ip_vs_conn_hashkey_conn(cp);
 
@@ -240,15 +243,13 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
        spin_lock(&cp->lock);
 
        if (cp->flags & IP_VS_CONN_F_HASHED) {
-               ret = false;
                /* Decrease refcnt and unlink conn only if we are last user */
                if (refcount_dec_if_one(&cp->refcnt)) {
                        hlist_del_rcu(&cp->c_list);
                        cp->flags &= ~IP_VS_CONN_F_HASHED;
                        ret = true;
                }
-       } else
-               ret = refcount_read(&cp->refcnt) ? false : true;
+       }
 
        spin_unlock(&cp->lock);
        ct_write_unlock_bh(hash);
@@ -454,12 +455,6 @@ ip_vs_conn_out_get_proto(struct netns_ipvs *ipvs, int af,
 }
 EXPORT_SYMBOL_GPL(ip_vs_conn_out_get_proto);
 
-static void __ip_vs_conn_put_notimer(struct ip_vs_conn *cp)
-{
-       __ip_vs_conn_put(cp);
-       ip_vs_conn_expire(&cp->timer);
-}
-
 /*
  *      Put back the conn and restart its timer with its timeout
  */
@@ -478,7 +473,7 @@ void ip_vs_conn_put(struct ip_vs_conn *cp)
            (refcount_read(&cp->refcnt) == 1) &&
            !timer_pending(&cp->timer))
                /* expire connection immediately */
-               __ip_vs_conn_put_notimer(cp);
+               ip_vs_conn_expire(&cp->timer);
        else
                __ip_vs_conn_put_timer(cp);
 }
index 5f6f73c..0679dd1 100644 (file)
@@ -119,6 +119,8 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
                struct ip_vs_cpu_stats *s;
                struct ip_vs_service *svc;
 
+               local_bh_disable();
+
                s = this_cpu_ptr(dest->stats.cpustats);
                u64_stats_update_begin(&s->syncp);
                s->cnt.inpkts++;
@@ -137,6 +139,8 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
                s->cnt.inpkts++;
                s->cnt.inbytes += skb->len;
                u64_stats_update_end(&s->syncp);
+
+               local_bh_enable();
        }
 }
 
@@ -151,6 +155,8 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
                struct ip_vs_cpu_stats *s;
                struct ip_vs_service *svc;
 
+               local_bh_disable();
+
                s = this_cpu_ptr(dest->stats.cpustats);
                u64_stats_update_begin(&s->syncp);
                s->cnt.outpkts++;
@@ -169,6 +175,8 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
                s->cnt.outpkts++;
                s->cnt.outbytes += skb->len;
                u64_stats_update_end(&s->syncp);
+
+               local_bh_enable();
        }
 }
 
@@ -179,6 +187,8 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
        struct netns_ipvs *ipvs = svc->ipvs;
        struct ip_vs_cpu_stats *s;
 
+       local_bh_disable();
+
        s = this_cpu_ptr(cp->dest->stats.cpustats);
        u64_stats_update_begin(&s->syncp);
        s->cnt.conns++;
@@ -193,6 +203,8 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
        u64_stats_update_begin(&s->syncp);
        s->cnt.conns++;
        u64_stats_update_end(&s->syncp);
+
+       local_bh_enable();
 }
 
 
index e97cdc1..8e67910 100644 (file)
@@ -981,6 +981,17 @@ static int tcp_packet(struct nf_conn *ct,
                        return NF_ACCEPT; /* Don't change state */
                }
                break;
+       case TCP_CONNTRACK_SYN_SENT2:
+               /* tcp_conntracks table is not smart enough to handle
+                * simultaneous open.
+                */
+               ct->proto.tcp.last_flags |= IP_CT_TCP_SIMULTANEOUS_OPEN;
+               break;
+       case TCP_CONNTRACK_SYN_RECV:
+               if (dir == IP_CT_DIR_REPLY && index == TCP_ACK_SET &&
+                   ct->proto.tcp.last_flags & IP_CT_TCP_SIMULTANEOUS_OPEN)
+                       new_state = TCP_CONNTRACK_ESTABLISHED;
+               break;
        case TCP_CONNTRACK_CLOSE:
                if (index == TCP_RST_SET
                    && (ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET)
index a2bb314..87b2a77 100644 (file)
@@ -169,6 +169,34 @@ static int nft_delchain(struct nft_ctx *ctx)
        return err;
 }
 
+static void nft_rule_expr_activate(const struct nft_ctx *ctx,
+                                  struct nft_rule *rule)
+{
+       struct nft_expr *expr;
+
+       expr = nft_expr_first(rule);
+       while (expr != nft_expr_last(rule) && expr->ops) {
+               if (expr->ops->activate)
+                       expr->ops->activate(ctx, expr);
+
+               expr = nft_expr_next(expr);
+       }
+}
+
+static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
+                                    struct nft_rule *rule)
+{
+       struct nft_expr *expr;
+
+       expr = nft_expr_first(rule);
+       while (expr != nft_expr_last(rule) && expr->ops) {
+               if (expr->ops->deactivate)
+                       expr->ops->deactivate(ctx, expr);
+
+               expr = nft_expr_next(expr);
+       }
+}
+
 static int
 nf_tables_delrule_deactivate(struct nft_ctx *ctx, struct nft_rule *rule)
 {
@@ -214,6 +242,7 @@ static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
                nft_trans_destroy(trans);
                return err;
        }
+       nft_rule_expr_deactivate(ctx, rule);
 
        return 0;
 }
@@ -2189,6 +2218,13 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
        kfree(rule);
 }
 
+static void nf_tables_rule_release(const struct nft_ctx *ctx,
+                                  struct nft_rule *rule)
+{
+       nft_rule_expr_deactivate(ctx, rule);
+       nf_tables_rule_destroy(ctx, rule);
+}
+
 #define NFT_RULE_MAXEXPRS      128
 
 static struct nft_expr_info *info;
@@ -2362,7 +2398,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
        return 0;
 
 err2:
-       nf_tables_rule_destroy(&ctx, rule);
+       nf_tables_rule_release(&ctx, rule);
 err1:
        for (i = 0; i < n; i++) {
                if (info[i].ops != NULL)
@@ -4045,8 +4081,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) ^
                            nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) ||
                            nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) ^
-                           nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF))
-                               return -EBUSY;
+                           nft_set_ext_exists(ext2, NFT_SET_EXT_OBJREF)) {
+                               err = -EBUSY;
+                               goto err5;
+                       }
                        if ((nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
                             nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) &&
                             memcmp(nft_set_ext_data(ext),
@@ -4132,7 +4170,7 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
  *     NFT_GOTO verdicts. This function must be called on active data objects
  *     from the second phase of the commit protocol.
  */
-static void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
+void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
 {
        if (type == NFT_DATA_VERDICT) {
                switch (data->verdict.code) {
@@ -5768,7 +5806,7 @@ static void nft_chain_commit_update(struct nft_trans *trans)
        }
 }
 
-static void nf_tables_commit_release(struct nft_trans *trans)
+static void nft_commit_release(struct nft_trans *trans)
 {
        switch (trans->msg_type) {
        case NFT_MSG_DELTABLE:
@@ -5797,6 +5835,21 @@ static void nf_tables_commit_release(struct nft_trans *trans)
        kfree(trans);
 }
 
+static void nf_tables_commit_release(struct net *net)
+{
+       struct nft_trans *trans, *next;
+
+       if (list_empty(&net->nft.commit_list))
+               return;
+
+       synchronize_rcu();
+
+       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+               list_del(&trans->list);
+               nft_commit_release(trans);
+       }
+}
+
 static int nf_tables_commit(struct net *net, struct sk_buff *skb)
 {
        struct nft_trans *trans, *next;
@@ -5927,13 +5980,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                }
        }
 
-       synchronize_rcu();
-
-       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
-               list_del(&trans->list);
-               nf_tables_commit_release(trans);
-       }
-
+       nf_tables_commit_release(net);
        nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
 
        return 0;
@@ -6013,10 +6060,12 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
                case NFT_MSG_NEWRULE:
                        trans->ctx.chain->use--;
                        list_del_rcu(&nft_trans_rule(trans)->list);
+                       nft_rule_expr_deactivate(&trans->ctx, nft_trans_rule(trans));
                        break;
                case NFT_MSG_DELRULE:
                        trans->ctx.chain->use++;
                        nft_clear(trans->ctx.net, nft_trans_rule(trans));
+                       nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans));
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWSET:
@@ -6592,7 +6641,7 @@ int __nft_release_basechain(struct nft_ctx *ctx)
        list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
                list_del(&rule->list);
                ctx->chain->use--;
-               nf_tables_rule_destroy(ctx, rule);
+               nf_tables_rule_release(ctx, rule);
        }
        list_del(&ctx->chain->list);
        ctx->table->use--;
@@ -6630,7 +6679,7 @@ static void __nft_release_tables(struct net *net)
                        list_for_each_entry_safe(rule, nr, &chain->rules, list) {
                                list_del(&rule->list);
                                chain->use--;
-                               nf_tables_rule_destroy(&ctx, rule);
+                               nf_tables_rule_release(&ctx, rule);
                        }
                }
                list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
index d457d85..4f46d2f 100644 (file)
@@ -113,15 +113,22 @@ DEFINE_STATIC_KEY_FALSE(nft_counters_enabled);
 static noinline void nft_update_chain_stats(const struct nft_chain *chain,
                                            const struct nft_pktinfo *pkt)
 {
+       struct nft_base_chain *base_chain;
        struct nft_stats *stats;
 
-       local_bh_disable();
-       stats = this_cpu_ptr(rcu_dereference(nft_base_chain(chain)->stats));
-       u64_stats_update_begin(&stats->syncp);
-       stats->pkts++;
-       stats->bytes += pkt->skb->len;
-       u64_stats_update_end(&stats->syncp);
-       local_bh_enable();
+       base_chain = nft_base_chain(chain);
+       if (!base_chain->stats)
+               return;
+
+       stats = this_cpu_ptr(rcu_dereference(base_chain->stats));
+       if (stats) {
+               local_bh_disable();
+               u64_stats_update_begin(&stats->syncp);
+               stats->pkts++;
+               stats->bytes += pkt->skb->len;
+               u64_stats_update_end(&stats->syncp);
+               local_bh_enable();
+       }
 }
 
 struct nft_jumpstack {
index b9505bc..6ddf891 100644 (file)
@@ -115,7 +115,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl,
                nfacct->flags = flags;
        }
 
-       strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX);
+       nla_strlcpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX);
 
        if (tb[NFACCT_BYTES]) {
                atomic64_set(&nfacct->bytes,
index 4a4b293..fa026b2 100644 (file)
@@ -149,8 +149,8 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
            !tb[NFCTH_POLICY_EXPECT_TIMEOUT])
                return -EINVAL;
 
-       strncpy(expect_policy->name,
-               nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN);
+       nla_strlcpy(expect_policy->name,
+                   nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN);
        expect_policy->max_expected =
                ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
        if (expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
@@ -234,7 +234,8 @@ nfnl_cthelper_create(const struct nlattr * const tb[],
        if (ret < 0)
                goto err1;
 
-       strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN);
+       nla_strlcpy(helper->name,
+                   nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN);
        size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
        if (size > FIELD_SIZEOF(struct nf_conn_help, data)) {
                ret = -ENOMEM;
index 8e23726..1d99a1e 100644 (file)
@@ -27,14 +27,31 @@ struct nft_xt {
        struct list_head        head;
        struct nft_expr_ops     ops;
        unsigned int            refcnt;
+
+       /* Unlike other expressions, ops doesn't have static storage duration.
+        * nft core assumes they do.  We use kfree_rcu so that nft core can
+        * can check expr->ops->size even after nft_compat->destroy() frees
+        * the nft_xt struct that holds the ops structure.
+        */
+       struct rcu_head         rcu_head;
+};
+
+/* Used for matches where *info is larger than X byte */
+#define NFT_MATCH_LARGE_THRESH 192
+
+struct nft_xt_match_priv {
+       void *info;
 };
 
-static void nft_xt_put(struct nft_xt *xt)
+static bool nft_xt_put(struct nft_xt *xt)
 {
        if (--xt->refcnt == 0) {
                list_del(&xt->head);
-               kfree(xt);
+               kfree_rcu(xt, rcu_head);
+               return true;
        }
+
+       return false;
 }
 
 static int nft_compat_chain_validate_dependency(const char *tablename,
@@ -226,6 +243,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
        struct xt_target *target = expr->ops->data;
        struct xt_tgchk_param par;
        size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO]));
+       struct nft_xt *nft_xt;
        u16 proto = 0;
        bool inv = false;
        union nft_entry e = {};
@@ -236,25 +254,22 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
        if (ctx->nla[NFTA_RULE_COMPAT]) {
                ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
                if (ret < 0)
-                       goto err;
+                       return ret;
        }
 
        nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
 
        ret = xt_check_target(&par, size, proto, inv);
        if (ret < 0)
-               goto err;
+               return ret;
 
        /* The standard target cannot be used */
-       if (target->target == NULL) {
-               ret = -EINVAL;
-               goto err;
-       }
+       if (!target->target)
+               return -EINVAL;
 
+       nft_xt = container_of(expr->ops, struct nft_xt, ops);
+       nft_xt->refcnt++;
        return 0;
-err:
-       module_put(target->me);
-       return ret;
 }
 
 static void
@@ -271,8 +286,8 @@ nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
        if (par.target->destroy != NULL)
                par.target->destroy(&par);
 
-       nft_xt_put(container_of(expr->ops, struct nft_xt, ops));
-       module_put(target->me);
+       if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
+               module_put(target->me);
 }
 
 static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr)
@@ -316,11 +331,11 @@ static int nft_target_validate(const struct nft_ctx *ctx,
        return 0;
 }
 
-static void nft_match_eval(const struct nft_expr *expr,
-                          struct nft_regs *regs,
-                          const struct nft_pktinfo *pkt)
+static void __nft_match_eval(const struct nft_expr *expr,
+                            struct nft_regs *regs,
+                            const struct nft_pktinfo *pkt,
+                            void *info)
 {
-       void *info = nft_expr_priv(expr);
        struct xt_match *match = expr->ops->data;
        struct sk_buff *skb = pkt->skb;
        bool ret;
@@ -344,6 +359,22 @@ static void nft_match_eval(const struct nft_expr *expr,
        }
 }
 
+static void nft_match_large_eval(const struct nft_expr *expr,
+                                struct nft_regs *regs,
+                                const struct nft_pktinfo *pkt)
+{
+       struct nft_xt_match_priv *priv = nft_expr_priv(expr);
+
+       __nft_match_eval(expr, regs, pkt, priv->info);
+}
+
+static void nft_match_eval(const struct nft_expr *expr,
+                          struct nft_regs *regs,
+                          const struct nft_pktinfo *pkt)
+{
+       __nft_match_eval(expr, regs, pkt, nft_expr_priv(expr));
+}
+
 static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = {
        [NFTA_MATCH_NAME]       = { .type = NLA_NUL_STRING },
        [NFTA_MATCH_REV]        = { .type = NLA_U32 },
@@ -404,13 +435,14 @@ static void match_compat_from_user(struct xt_match *m, void *in, void *out)
 }
 
 static int
-nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
-               const struct nlattr * const tb[])
+__nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+                const struct nlattr * const tb[],
+                void *info)
 {
-       void *info = nft_expr_priv(expr);
        struct xt_match *match = expr->ops->data;
        struct xt_mtchk_param par;
        size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO]));
+       struct nft_xt *nft_xt;
        u16 proto = 0;
        bool inv = false;
        union nft_entry e = {};
@@ -421,26 +453,50 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
        if (ctx->nla[NFTA_RULE_COMPAT]) {
                ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv);
                if (ret < 0)
-                       goto err;
+                       return ret;
        }
 
        nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
 
        ret = xt_check_match(&par, size, proto, inv);
        if (ret < 0)
-               goto err;
+               return ret;
 
+       nft_xt = container_of(expr->ops, struct nft_xt, ops);
+       nft_xt->refcnt++;
        return 0;
-err:
-       module_put(match->me);
+}
+
+static int
+nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+              const struct nlattr * const tb[])
+{
+       return __nft_match_init(ctx, expr, tb, nft_expr_priv(expr));
+}
+
+static int
+nft_match_large_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
+                    const struct nlattr * const tb[])
+{
+       struct nft_xt_match_priv *priv = nft_expr_priv(expr);
+       struct xt_match *m = expr->ops->data;
+       int ret;
+
+       priv->info = kmalloc(XT_ALIGN(m->matchsize), GFP_KERNEL);
+       if (!priv->info)
+               return -ENOMEM;
+
+       ret = __nft_match_init(ctx, expr, tb, priv->info);
+       if (ret)
+               kfree(priv->info);
        return ret;
 }
 
 static void
-nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
+__nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,
+                   void *info)
 {
        struct xt_match *match = expr->ops->data;
-       void *info = nft_expr_priv(expr);
        struct xt_mtdtor_param par;
 
        par.net = ctx->net;
@@ -450,13 +506,28 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
        if (par.match->destroy != NULL)
                par.match->destroy(&par);
 
-       nft_xt_put(container_of(expr->ops, struct nft_xt, ops));
-       module_put(match->me);
+       if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
+               module_put(match->me);
 }
 
-static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr)
+static void
+nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
+{
+       __nft_match_destroy(ctx, expr, nft_expr_priv(expr));
+}
+
+static void
+nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
+{
+       struct nft_xt_match_priv *priv = nft_expr_priv(expr);
+
+       __nft_match_destroy(ctx, expr, priv->info);
+       kfree(priv->info);
+}
+
+static int __nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr,
+                           void *info)
 {
-       void *info = nft_expr_priv(expr);
        struct xt_match *match = expr->ops->data;
 
        if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) ||
@@ -470,6 +541,18 @@ nla_put_failure:
        return -1;
 }
 
+static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       return __nft_match_dump(skb, expr, nft_expr_priv(expr));
+}
+
+static int nft_match_large_dump(struct sk_buff *skb, const struct nft_expr *e)
+{
+       struct nft_xt_match_priv *priv = nft_expr_priv(e);
+
+       return __nft_match_dump(skb, e, priv->info);
+}
+
 static int nft_match_validate(const struct nft_ctx *ctx,
                              const struct nft_expr *expr,
                              const struct nft_data **data)
@@ -637,6 +720,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
 {
        struct nft_xt *nft_match;
        struct xt_match *match;
+       unsigned int matchsize;
        char *mt_name;
        u32 rev, family;
        int err;
@@ -654,13 +738,8 @@ nft_match_select_ops(const struct nft_ctx *ctx,
        list_for_each_entry(nft_match, &nft_match_list, head) {
                struct xt_match *match = nft_match->ops.data;
 
-               if (nft_match_cmp(match, mt_name, rev, family)) {
-                       if (!try_module_get(match->me))
-                               return ERR_PTR(-ENOENT);
-
-                       nft_match->refcnt++;
+               if (nft_match_cmp(match, mt_name, rev, family))
                        return &nft_match->ops;
-               }
        }
 
        match = xt_request_find_match(family, mt_name, rev);
@@ -679,9 +758,8 @@ nft_match_select_ops(const struct nft_ctx *ctx,
                goto err;
        }
 
-       nft_match->refcnt = 1;
+       nft_match->refcnt = 0;
        nft_match->ops.type = &nft_match_type;
-       nft_match->ops.size = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize));
        nft_match->ops.eval = nft_match_eval;
        nft_match->ops.init = nft_match_init;
        nft_match->ops.destroy = nft_match_destroy;
@@ -689,6 +767,18 @@ nft_match_select_ops(const struct nft_ctx *ctx,
        nft_match->ops.validate = nft_match_validate;
        nft_match->ops.data = match;
 
+       matchsize = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize));
+       if (matchsize > NFT_MATCH_LARGE_THRESH) {
+               matchsize = NFT_EXPR_SIZE(sizeof(struct nft_xt_match_priv));
+
+               nft_match->ops.eval = nft_match_large_eval;
+               nft_match->ops.init = nft_match_large_init;
+               nft_match->ops.destroy = nft_match_large_destroy;
+               nft_match->ops.dump = nft_match_large_dump;
+       }
+
+       nft_match->ops.size = matchsize;
+
        list_add(&nft_match->head, &nft_match_list);
 
        return &nft_match->ops;
@@ -739,13 +829,8 @@ nft_target_select_ops(const struct nft_ctx *ctx,
        list_for_each_entry(nft_target, &nft_target_list, head) {
                struct xt_target *target = nft_target->ops.data;
 
-               if (nft_target_cmp(target, tg_name, rev, family)) {
-                       if (!try_module_get(target->me))
-                               return ERR_PTR(-ENOENT);
-
-                       nft_target->refcnt++;
+               if (nft_target_cmp(target, tg_name, rev, family))
                        return &nft_target->ops;
-               }
        }
 
        target = xt_request_find_target(family, tg_name, rev);
@@ -764,7 +849,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
                goto err;
        }
 
-       nft_target->refcnt = 1;
+       nft_target->refcnt = 0;
        nft_target->ops.type = &nft_target_type;
        nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
        nft_target->ops.init = nft_target_init;
@@ -823,6 +908,32 @@ err_match:
 
 static void __exit nft_compat_module_exit(void)
 {
+       struct nft_xt *xt, *next;
+
+       /* list should be empty here, it can be non-empty only in case there
+        * was an error that caused nft_xt expr to not be initialized fully
+        * and noone else requested the same expression later.
+        *
+        * In this case, the lists contain 0-refcount entries that still
+        * hold module reference.
+        */
+       list_for_each_entry_safe(xt, next, &nft_target_list, head) {
+               struct xt_target *target = xt->ops.data;
+
+               if (WARN_ON_ONCE(xt->refcnt))
+                       continue;
+               module_put(target->me);
+               kfree(xt);
+       }
+
+       list_for_each_entry_safe(xt, next, &nft_match_list, head) {
+               struct xt_match *match = xt->ops.data;
+
+               if (WARN_ON_ONCE(xt->refcnt))
+                       continue;
+               module_put(match->me);
+               kfree(xt);
+       }
        nfnetlink_subsys_unregister(&nfnl_compat_subsys);
        nft_unregister_expr(&nft_target_type);
        nft_unregister_expr(&nft_match_type);
index 4717d77..aa87ff8 100644 (file)
@@ -69,8 +69,16 @@ err1:
        return err;
 }
 
-static void nft_immediate_destroy(const struct nft_ctx *ctx,
-                                 const struct nft_expr *expr)
+static void nft_immediate_activate(const struct nft_ctx *ctx,
+                                  const struct nft_expr *expr)
+{
+       const struct nft_immediate_expr *priv = nft_expr_priv(expr);
+
+       return nft_data_hold(&priv->data, nft_dreg_to_type(priv->dreg));
+}
+
+static void nft_immediate_deactivate(const struct nft_ctx *ctx,
+                                    const struct nft_expr *expr)
 {
        const struct nft_immediate_expr *priv = nft_expr_priv(expr);
 
@@ -108,7 +116,8 @@ static const struct nft_expr_ops nft_imm_ops = {
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)),
        .eval           = nft_immediate_eval,
        .init           = nft_immediate_init,
-       .destroy        = nft_immediate_destroy,
+       .activate       = nft_immediate_activate,
+       .deactivate     = nft_immediate_deactivate,
        .dump           = nft_immediate_dump,
        .validate       = nft_immediate_validate,
 };
index 71325fe..cb7cb30 100644 (file)
@@ -183,6 +183,9 @@ struct xt_match *xt_find_match(u8 af, const char *name, u8 revision)
        struct xt_match *m;
        int err = -ENOENT;
 
+       if (strnlen(name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
+               return ERR_PTR(-EINVAL);
+
        mutex_lock(&xt[af].mutex);
        list_for_each_entry(m, &xt[af].match, list) {
                if (strcmp(m->name, name) == 0) {
@@ -229,6 +232,9 @@ struct xt_target *xt_find_target(u8 af, const char *name, u8 revision)
        struct xt_target *t;
        int err = -ENOENT;
 
+       if (strnlen(name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
+               return ERR_PTR(-EINVAL);
+
        mutex_lock(&xt[af].mutex);
        list_for_each_entry(t, &xt[af].target, list) {
                if (strcmp(t->name, name) == 0) {
index 611a26d..2cc98c7 100644 (file)
@@ -2871,13 +2871,15 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
        if (skb == NULL)
                goto out_unlock;
 
-       skb_set_network_header(skb, reserve);
+       skb_reset_network_header(skb);
 
        err = -EINVAL;
        if (sock->type == SOCK_DGRAM) {
                offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len);
                if (unlikely(offset < 0))
                        goto out_free;
+       } else if (reserve) {
+               skb_push(skb, reserve);
        }
 
        /* Returns -EFAULT on error */
index 59d0eb9..a7a4e6f 100644 (file)
@@ -178,9 +178,10 @@ static void rfkill_led_trigger_unregister(struct rfkill *rfkill)
 }
 
 static struct led_trigger rfkill_any_led_trigger;
-static struct work_struct rfkill_any_work;
+static struct led_trigger rfkill_none_led_trigger;
+static struct work_struct rfkill_global_led_trigger_work;
 
-static void rfkill_any_led_trigger_worker(struct work_struct *work)
+static void rfkill_global_led_trigger_worker(struct work_struct *work)
 {
        enum led_brightness brightness = LED_OFF;
        struct rfkill *rfkill;
@@ -195,30 +196,43 @@ static void rfkill_any_led_trigger_worker(struct work_struct *work)
        mutex_unlock(&rfkill_global_mutex);
 
        led_trigger_event(&rfkill_any_led_trigger, brightness);
+       led_trigger_event(&rfkill_none_led_trigger,
+                         brightness == LED_OFF ? LED_FULL : LED_OFF);
 }
 
-static void rfkill_any_led_trigger_event(void)
+static void rfkill_global_led_trigger_event(void)
 {
-       schedule_work(&rfkill_any_work);
+       schedule_work(&rfkill_global_led_trigger_work);
 }
 
-static void rfkill_any_led_trigger_activate(struct led_classdev *led_cdev)
+static int rfkill_global_led_trigger_register(void)
 {
-       rfkill_any_led_trigger_event();
-}
+       int ret;
+
+       INIT_WORK(&rfkill_global_led_trigger_work,
+                       rfkill_global_led_trigger_worker);
 
-static int rfkill_any_led_trigger_register(void)
-{
-       INIT_WORK(&rfkill_any_work, rfkill_any_led_trigger_worker);
        rfkill_any_led_trigger.name = "rfkill-any";
-       rfkill_any_led_trigger.activate = rfkill_any_led_trigger_activate;
-       return led_trigger_register(&rfkill_any_led_trigger);
+       ret = led_trigger_register(&rfkill_any_led_trigger);
+       if (ret)
+               return ret;
+
+       rfkill_none_led_trigger.name = "rfkill-none";
+       ret = led_trigger_register(&rfkill_none_led_trigger);
+       if (ret)
+               led_trigger_unregister(&rfkill_any_led_trigger);
+       else
+               /* Delay activation until all global triggers are registered */
+               rfkill_global_led_trigger_event();
+
+       return ret;
 }
 
-static void rfkill_any_led_trigger_unregister(void)
+static void rfkill_global_led_trigger_unregister(void)
 {
+       led_trigger_unregister(&rfkill_none_led_trigger);
        led_trigger_unregister(&rfkill_any_led_trigger);
-       cancel_work_sync(&rfkill_any_work);
+       cancel_work_sync(&rfkill_global_led_trigger_work);
 }
 #else
 static void rfkill_led_trigger_event(struct rfkill *rfkill)
@@ -234,16 +248,16 @@ static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill)
 {
 }
 
-static void rfkill_any_led_trigger_event(void)
+static void rfkill_global_led_trigger_event(void)
 {
 }
 
-static int rfkill_any_led_trigger_register(void)
+static int rfkill_global_led_trigger_register(void)
 {
        return 0;
 }
 
-static void rfkill_any_led_trigger_unregister(void)
+static void rfkill_global_led_trigger_unregister(void)
 {
 }
 #endif /* CONFIG_RFKILL_LEDS */
@@ -354,7 +368,7 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
        spin_unlock_irqrestore(&rfkill->lock, flags);
 
        rfkill_led_trigger_event(rfkill);
-       rfkill_any_led_trigger_event();
+       rfkill_global_led_trigger_event();
 
        if (prev != curr)
                rfkill_event(rfkill);
@@ -535,7 +549,7 @@ bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
        spin_unlock_irqrestore(&rfkill->lock, flags);
 
        rfkill_led_trigger_event(rfkill);
-       rfkill_any_led_trigger_event();
+       rfkill_global_led_trigger_event();
 
        if (rfkill->registered && prev != blocked)
                schedule_work(&rfkill->uevent_work);
@@ -579,7 +593,7 @@ bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
                schedule_work(&rfkill->uevent_work);
 
        rfkill_led_trigger_event(rfkill);
-       rfkill_any_led_trigger_event();
+       rfkill_global_led_trigger_event();
 
        return blocked;
 }
@@ -629,7 +643,7 @@ void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
                        schedule_work(&rfkill->uevent_work);
 
                rfkill_led_trigger_event(rfkill);
-               rfkill_any_led_trigger_event();
+               rfkill_global_led_trigger_event();
        }
 }
 EXPORT_SYMBOL(rfkill_set_states);
@@ -1046,7 +1060,7 @@ int __must_check rfkill_register(struct rfkill *rfkill)
 #endif
        }
 
-       rfkill_any_led_trigger_event();
+       rfkill_global_led_trigger_event();
        rfkill_send_events(rfkill, RFKILL_OP_ADD);
 
        mutex_unlock(&rfkill_global_mutex);
@@ -1079,7 +1093,7 @@ void rfkill_unregister(struct rfkill *rfkill)
        mutex_lock(&rfkill_global_mutex);
        rfkill_send_events(rfkill, RFKILL_OP_DEL);
        list_del_init(&rfkill->node);
-       rfkill_any_led_trigger_event();
+       rfkill_global_led_trigger_event();
        mutex_unlock(&rfkill_global_mutex);
 
        rfkill_led_trigger_unregister(rfkill);
@@ -1332,7 +1346,7 @@ static int __init rfkill_init(void)
        if (error)
                goto error_misc;
 
-       error = rfkill_any_led_trigger_register();
+       error = rfkill_global_led_trigger_register();
        if (error)
                goto error_led_trigger;
 
@@ -1346,7 +1360,7 @@ static int __init rfkill_init(void)
 
 #ifdef CONFIG_RFKILL_INPUT
 error_input:
-       rfkill_any_led_trigger_unregister();
+       rfkill_global_led_trigger_unregister();
 #endif
 error_led_trigger:
        misc_deregister(&rfkill_miscdev);
@@ -1362,7 +1376,7 @@ static void __exit rfkill_exit(void)
 #ifdef CONFIG_RFKILL_INPUT
        rfkill_handler_exit();
 #endif
-       rfkill_any_led_trigger_unregister();
+       rfkill_global_led_trigger_unregister();
        misc_deregister(&rfkill_miscdev);
        class_unregister(&rfkill_class);
 }
index 7225124..3f4cf93 100644 (file)
@@ -77,9 +77,9 @@ static void free_tcf(struct tc_action *p)
 
 static void tcf_idr_remove(struct tcf_idrinfo *idrinfo, struct tc_action *p)
 {
-       spin_lock_bh(&idrinfo->lock);
+       spin_lock(&idrinfo->lock);
        idr_remove(&idrinfo->action_idr, p->tcfa_index);
-       spin_unlock_bh(&idrinfo->lock);
+       spin_unlock(&idrinfo->lock);
        gen_kill_estimator(&p->tcfa_rate_est);
        free_tcf(p);
 }
@@ -156,7 +156,7 @@ static int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
        struct tc_action *p;
        unsigned long id = 1;
 
-       spin_lock_bh(&idrinfo->lock);
+       spin_lock(&idrinfo->lock);
 
        s_i = cb->args[0];
 
@@ -191,7 +191,7 @@ done:
        if (index >= 0)
                cb->args[0] = index + 1;
 
-       spin_unlock_bh(&idrinfo->lock);
+       spin_unlock(&idrinfo->lock);
        if (n_i) {
                if (act_flags & TCA_FLAG_LARGE_DUMP_ON)
                        cb->args[1] = n_i;
@@ -261,9 +261,9 @@ static struct tc_action *tcf_idr_lookup(u32 index, struct tcf_idrinfo *idrinfo)
 {
        struct tc_action *p = NULL;
 
-       spin_lock_bh(&idrinfo->lock);
+       spin_lock(&idrinfo->lock);
        p = idr_find(&idrinfo->action_idr, index);
-       spin_unlock_bh(&idrinfo->lock);
+       spin_unlock(&idrinfo->lock);
 
        return p;
 }
@@ -323,7 +323,7 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
        }
        spin_lock_init(&p->tcfa_lock);
        idr_preload(GFP_KERNEL);
-       spin_lock_bh(&idrinfo->lock);
+       spin_lock(&idrinfo->lock);
        /* user doesn't specify an index */
        if (!index) {
                index = 1;
@@ -331,7 +331,7 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
        } else {
                err = idr_alloc_u32(idr, NULL, &index, index, GFP_ATOMIC);
        }
-       spin_unlock_bh(&idrinfo->lock);
+       spin_unlock(&idrinfo->lock);
        idr_preload_end();
        if (err)
                goto err3;
@@ -369,9 +369,9 @@ void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a)
 {
        struct tcf_idrinfo *idrinfo = tn->idrinfo;
 
-       spin_lock_bh(&idrinfo->lock);
+       spin_lock(&idrinfo->lock);
        idr_replace(&idrinfo->action_idr, a, a->tcfa_index);
-       spin_unlock_bh(&idrinfo->lock);
+       spin_unlock(&idrinfo->lock);
 }
 EXPORT_SYMBOL(tcf_idr_insert);
 
index 8536046..1fb39e1 100644 (file)
@@ -161,6 +161,8 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla,
                        case htons(ETH_P_8021AD):
                                break;
                        default:
+                               if (exists)
+                                       tcf_idr_release(*a, bind);
                                return -EPROTONOSUPPORT;
                        }
                } else {
index 39c144b..760ab1b 100644 (file)
@@ -373,33 +373,24 @@ bool sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
  */
 static inline bool qdisc_restart(struct Qdisc *q, int *packets)
 {
-       bool more, validate, nolock = q->flags & TCQ_F_NOLOCK;
        spinlock_t *root_lock = NULL;
        struct netdev_queue *txq;
        struct net_device *dev;
        struct sk_buff *skb;
+       bool validate;
 
        /* Dequeue packet */
-       if (nolock && test_and_set_bit(__QDISC_STATE_RUNNING, &q->state))
-               return false;
-
        skb = dequeue_skb(q, &validate, packets);
-       if (unlikely(!skb)) {
-               if (nolock)
-                       clear_bit(__QDISC_STATE_RUNNING, &q->state);
+       if (unlikely(!skb))
                return false;
-       }
 
-       if (!nolock)
+       if (!(q->flags & TCQ_F_NOLOCK))
                root_lock = qdisc_lock(q);
 
        dev = qdisc_dev(q);
        txq = skb_get_tx_queue(dev, skb);
 
-       more = sch_direct_xmit(skb, q, dev, txq, root_lock, validate);
-       if (nolock)
-               clear_bit(__QDISC_STATE_RUNNING, &q->state);
-       return more;
+       return sch_direct_xmit(skb, q, dev, txq, root_lock, validate);
 }
 
 void __qdisc_run(struct Qdisc *q)
@@ -665,7 +656,7 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc)
                if (__skb_array_empty(q))
                        continue;
 
-               skb = skb_array_consume_bh(q);
+               skb = __skb_array_consume(q);
        }
        if (likely(skb)) {
                qdisc_qstats_cpu_backlog_dec(qdisc, skb);
@@ -706,7 +697,7 @@ static void pfifo_fast_reset(struct Qdisc *qdisc)
                if (!q->ring.queue)
                        continue;
 
-               while ((skb = skb_array_consume_bh(q)) != NULL)
+               while ((skb = __skb_array_consume(q)) != NULL)
                        kfree_skb(skb);
        }
 
@@ -867,6 +858,11 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
        lockdep_set_class(&sch->busylock,
                          dev->qdisc_tx_busylock ?: &qdisc_tx_busylock);
 
+       /* seqlock has the same scope of busylock, for NOLOCK qdisc */
+       spin_lock_init(&sch->seqlock);
+       lockdep_set_class(&sch->busylock,
+                         dev->qdisc_tx_busylock ?: &qdisc_tx_busylock);
+
        seqcount_init(&sch->running);
        lockdep_set_class(&sch->running,
                          dev->qdisc_running_key ?: &qdisc_running_key);
@@ -1106,6 +1102,10 @@ static void dev_deactivate_queue(struct net_device *dev,
 
        qdisc = rtnl_dereference(dev_queue->qdisc);
        if (qdisc) {
+               bool nolock = qdisc->flags & TCQ_F_NOLOCK;
+
+               if (nolock)
+                       spin_lock_bh(&qdisc->seqlock);
                spin_lock_bh(qdisc_lock(qdisc));
 
                if (!(qdisc->flags & TCQ_F_BUILTIN))
@@ -1115,6 +1115,8 @@ static void dev_deactivate_queue(struct net_device *dev,
                qdisc_reset(qdisc);
 
                spin_unlock_bh(qdisc_lock(qdisc));
+               if (nolock)
+                       spin_unlock_bh(&qdisc->seqlock);
        }
 }
 
@@ -1131,17 +1133,13 @@ static bool some_qdisc_is_busy(struct net_device *dev)
                dev_queue = netdev_get_tx_queue(dev, i);
                q = dev_queue->qdisc_sleeping;
 
-               if (q->flags & TCQ_F_NOLOCK) {
-                       val = test_bit(__QDISC_STATE_SCHED, &q->state);
-               } else {
-                       root_lock = qdisc_lock(q);
-                       spin_lock_bh(root_lock);
+               root_lock = qdisc_lock(q);
+               spin_lock_bh(root_lock);
 
-                       val = (qdisc_is_running(q) ||
-                              test_bit(__QDISC_STATE_SCHED, &q->state));
+               val = (qdisc_is_running(q) ||
+                      test_bit(__QDISC_STATE_SCHED, &q->state));
 
-                       spin_unlock_bh(root_lock);
-               }
+               spin_unlock_bh(root_lock);
 
                if (val)
                        return true;
index 16644b3..56c181c 100644 (file)
@@ -222,10 +222,11 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt,
                                         extack);
                if (IS_ERR(child))
                        return PTR_ERR(child);
-       }
 
-       if (child != &noop_qdisc)
+               /* child is fifo, no need to check for noop_qdisc */
                qdisc_hash_add(child, true);
+       }
+
        sch_tree_lock(sch);
        q->flags = ctl->flags;
        q->limit = ctl->limit;
index 03225a8..6f74a42 100644 (file)
@@ -383,6 +383,9 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt,
                        err = PTR_ERR(child);
                        goto done;
                }
+
+               /* child is fifo, no need to check for noop_qdisc */
+               qdisc_hash_add(child, true);
        }
 
        sch_tree_lock(sch);
@@ -391,8 +394,6 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt,
                                          q->qdisc->qstats.backlog);
                qdisc_destroy(q->qdisc);
                q->qdisc = child;
-               if (child != &noop_qdisc)
-                       qdisc_hash_add(child, true);
        }
        q->limit = qopt->limit;
        if (tb[TCA_TBF_PBURST])
index dee7cbd..d68aa33 100644 (file)
@@ -601,14 +601,14 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
 
 /*
  * Transmit DATA chunks on the retransmit queue.  Upon return from
- * sctp_outq_flush_rtx() the packet 'pkt' may contain chunks which
+ * __sctp_outq_flush_rtx() the packet 'pkt' may contain chunks which
  * need to be transmitted by the caller.
  * We assume that pkt->transport has already been set.
  *
  * The return value is a normal kernel error return value.
  */
-static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
-                              int rtx_timeout, int *start_timer)
+static int __sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
+                                int rtx_timeout, int *start_timer, gfp_t gfp)
 {
        struct sctp_transport *transport = pkt->transport;
        struct sctp_chunk *chunk, *chunk1;
@@ -684,12 +684,12 @@ redo:
                                 * control chunks are already freed so there
                                 * is nothing we can do.
                                 */
-                               sctp_packet_transmit(pkt, GFP_ATOMIC);
+                               sctp_packet_transmit(pkt, gfp);
                                goto redo;
                        }
 
                        /* Send this packet.  */
-                       error = sctp_packet_transmit(pkt, GFP_ATOMIC);
+                       error = sctp_packet_transmit(pkt, gfp);
 
                        /* If we are retransmitting, we should only
                         * send a single packet.
@@ -705,7 +705,7 @@ redo:
 
                case SCTP_XMIT_RWND_FULL:
                        /* Send this packet. */
-                       error = sctp_packet_transmit(pkt, GFP_ATOMIC);
+                       error = sctp_packet_transmit(pkt, gfp);
 
                        /* Stop sending DATA as there is no more room
                         * at the receiver.
@@ -715,7 +715,7 @@ redo:
 
                case SCTP_XMIT_DELAY:
                        /* Send this packet. */
-                       error = sctp_packet_transmit(pkt, GFP_ATOMIC);
+                       error = sctp_packet_transmit(pkt, gfp);
 
                        /* Stop sending DATA because of nagle delay. */
                        done = 1;
@@ -776,68 +776,43 @@ void sctp_outq_uncork(struct sctp_outq *q, gfp_t gfp)
        sctp_outq_flush(q, 0, gfp);
 }
 
-
-/*
- * Try to flush an outqueue.
- *
- * Description: Send everything in q which we legally can, subject to
- * congestion limitations.
- * * Note: This function can be called from multiple contexts so appropriate
- * locking concerns must be made.  Today we use the sock lock to protect
- * this function.
- */
-static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
+static int sctp_packet_singleton(struct sctp_transport *transport,
+                                struct sctp_chunk *chunk, gfp_t gfp)
 {
-       struct sctp_packet *packet;
+       const struct sctp_association *asoc = transport->asoc;
+       const __u16 sport = asoc->base.bind_addr.port;
+       const __u16 dport = asoc->peer.port;
+       const __u32 vtag = asoc->peer.i.init_tag;
        struct sctp_packet singleton;
-       struct sctp_association *asoc = q->asoc;
-       __u16 sport = asoc->base.bind_addr.port;
-       __u16 dport = asoc->peer.port;
-       __u32 vtag = asoc->peer.i.init_tag;
-       struct sctp_transport *transport = NULL;
-       struct sctp_transport *new_transport;
-       struct sctp_chunk *chunk, *tmp;
-       enum sctp_xmit status;
-       int error = 0;
-       int start_timer = 0;
-       int one_packet = 0;
 
+       sctp_packet_init(&singleton, transport, sport, dport);
+       sctp_packet_config(&singleton, vtag, 0);
+       sctp_packet_append_chunk(&singleton, chunk);
+       return sctp_packet_transmit(&singleton, gfp);
+}
+
+/* Struct to hold the context during sctp outq flush */
+struct sctp_flush_ctx {
+       struct sctp_outq *q;
+       /* Current transport being used. It's NOT the same as curr active one */
+       struct sctp_transport *transport;
        /* These transports have chunks to send. */
        struct list_head transport_list;
-       struct list_head *ltransport;
-
-       INIT_LIST_HEAD(&transport_list);
-       packet = NULL;
-
-       /*
-        * 6.10 Bundling
-        *   ...
-        *   When bundling control chunks with DATA chunks, an
-        *   endpoint MUST place control chunks first in the outbound
-        *   SCTP packet.  The transmitter MUST transmit DATA chunks
-        *   within a SCTP packet in increasing order of TSN.
-        *   ...
-        */
-
-       list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) {
-               /* RFC 5061, 5.3
-                * F1) This means that until such time as the ASCONF
-                * containing the add is acknowledged, the sender MUST
-                * NOT use the new IP address as a source for ANY SCTP
-                * packet except on carrying an ASCONF Chunk.
-                */
-               if (asoc->src_out_of_asoc_ok &&
-                   chunk->chunk_hdr->type != SCTP_CID_ASCONF)
-                       continue;
-
-               list_del_init(&chunk->list);
+       struct sctp_association *asoc;
+       /* Packet on the current transport above */
+       struct sctp_packet *packet;
+       gfp_t gfp;
+};
 
-               /* Pick the right transport to use. */
-               new_transport = chunk->transport;
+/* transport: current transport */
+static void sctp_outq_select_transport(struct sctp_flush_ctx *ctx,
+                                      struct sctp_chunk *chunk)
+{
+       struct sctp_transport *new_transport = chunk->transport;
 
-               if (!new_transport) {
-                       /*
-                        * If we have a prior transport pointer, see if
+       if (!new_transport) {
+               if (!sctp_chunk_is_data(chunk)) {
+                       /* If we have a prior transport pointer, see if
                         * the destination address of the chunk
                         * matches the destination address of the
                         * current transport.  If not a match, then
@@ -846,22 +821,26 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
                         * after processing ASCONFs, we may have new
                         * transports created.
                         */
-                       if (transport &&
-                           sctp_cmp_addr_exact(&chunk->dest,
-                                               &transport->ipaddr))
-                                       new_transport = transport;
+                       if (ctx->transport && sctp_cmp_addr_exact(&chunk->dest,
+                                                       &ctx->transport->ipaddr))
+                               new_transport = ctx->transport;
                        else
-                               new_transport = sctp_assoc_lookup_paddr(asoc,
-                                                               &chunk->dest);
+                               new_transport = sctp_assoc_lookup_paddr(ctx->asoc,
+                                                                 &chunk->dest);
+               }
 
-                       /* if we still don't have a new transport, then
-                        * use the current active path.
-                        */
-                       if (!new_transport)
-                               new_transport = asoc->peer.active_path;
-               } else if ((new_transport->state == SCTP_INACTIVE) ||
-                          (new_transport->state == SCTP_UNCONFIRMED) ||
-                          (new_transport->state == SCTP_PF)) {
+               /* if we still don't have a new transport, then
+                * use the current active path.
+                */
+               if (!new_transport)
+                       new_transport = ctx->asoc->peer.active_path;
+       } else {
+               __u8 type;
+
+               switch (new_transport->state) {
+               case SCTP_INACTIVE:
+               case SCTP_UNCONFIRMED:
+               case SCTP_PF:
                        /* If the chunk is Heartbeat or Heartbeat Ack,
                         * send it to chunk->transport, even if it's
                         * inactive.
@@ -875,29 +854,64 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
                         *
                         * ASCONF_ACKs also must be sent to the source.
                         */
-                       if (chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT &&
-                           chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT_ACK &&
-                           chunk->chunk_hdr->type != SCTP_CID_ASCONF_ACK)
-                               new_transport = asoc->peer.active_path;
+                       type = chunk->chunk_hdr->type;
+                       if (type != SCTP_CID_HEARTBEAT &&
+                           type != SCTP_CID_HEARTBEAT_ACK &&
+                           type != SCTP_CID_ASCONF_ACK)
+                               new_transport = ctx->asoc->peer.active_path;
+                       break;
+               default:
+                       break;
                }
+       }
 
-               /* Are we switching transports?
-                * Take care of transport locks.
+       /* Are we switching transports? Take care of transport locks. */
+       if (new_transport != ctx->transport) {
+               ctx->transport = new_transport;
+               ctx->packet = &ctx->transport->packet;
+
+               if (list_empty(&ctx->transport->send_ready))
+                       list_add_tail(&ctx->transport->send_ready,
+                                     &ctx->transport_list);
+
+               sctp_packet_config(ctx->packet,
+                                  ctx->asoc->peer.i.init_tag,
+                                  ctx->asoc->peer.ecn_capable);
+               /* We've switched transports, so apply the
+                * Burst limit to the new transport.
                 */
-               if (new_transport != transport) {
-                       transport = new_transport;
-                       if (list_empty(&transport->send_ready)) {
-                               list_add_tail(&transport->send_ready,
-                                             &transport_list);
-                       }
-                       packet = &transport->packet;
-                       sctp_packet_config(packet, vtag,
-                                          asoc->peer.ecn_capable);
-               }
+               sctp_transport_burst_limited(ctx->transport);
+       }
+}
+
+static void sctp_outq_flush_ctrl(struct sctp_flush_ctx *ctx)
+{
+       struct sctp_chunk *chunk, *tmp;
+       enum sctp_xmit status;
+       int one_packet, error;
+
+       list_for_each_entry_safe(chunk, tmp, &ctx->q->control_chunk_list, list) {
+               one_packet = 0;
+
+               /* RFC 5061, 5.3
+                * F1) This means that until such time as the ASCONF
+                * containing the add is acknowledged, the sender MUST
+                * NOT use the new IP address as a source for ANY SCTP
+                * packet except on carrying an ASCONF Chunk.
+                */
+               if (ctx->asoc->src_out_of_asoc_ok &&
+                   chunk->chunk_hdr->type != SCTP_CID_ASCONF)
+                       continue;
+
+               list_del_init(&chunk->list);
+
+               /* Pick the right transport to use. Should always be true for
+                * the first chunk as we don't have a transport by then.
+                */
+               sctp_outq_select_transport(ctx, chunk);
 
                switch (chunk->chunk_hdr->type) {
-               /*
-                * 6.10 Bundling
+               /* 6.10 Bundling
                 *   ...
                 *   An endpoint MUST NOT bundle INIT, INIT ACK or SHUTDOWN
                 *   COMPLETE with any other chunks.  [Send them immediately.]
@@ -905,20 +919,19 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
                case SCTP_CID_INIT:
                case SCTP_CID_INIT_ACK:
                case SCTP_CID_SHUTDOWN_COMPLETE:
-                       sctp_packet_init(&singleton, transport, sport, dport);
-                       sctp_packet_config(&singleton, vtag, 0);
-                       sctp_packet_append_chunk(&singleton, chunk);
-                       error = sctp_packet_transmit(&singleton, gfp);
+                       error = sctp_packet_singleton(ctx->transport, chunk,
+                                                     ctx->gfp);
                        if (error < 0) {
-                               asoc->base.sk->sk_err = -error;
+                               ctx->asoc->base.sk->sk_err = -error;
                                return;
                        }
                        break;
 
                case SCTP_CID_ABORT:
                        if (sctp_test_T_bit(chunk))
-                               packet->vtag = asoc->c.my_vtag;
+                               ctx->packet->vtag = ctx->asoc->c.my_vtag;
                        /* fallthru */
+
                /* The following chunks are "response" chunks, i.e.
                 * they are generated in response to something we
                 * received.  If we are sending these, then we can
@@ -942,27 +955,27 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
                case SCTP_CID_FWD_TSN:
                case SCTP_CID_I_FWD_TSN:
                case SCTP_CID_RECONF:
-                       status = sctp_packet_transmit_chunk(packet, chunk,
-                                                           one_packet, gfp);
-                       if (status  != SCTP_XMIT_OK) {
+                       status = sctp_packet_transmit_chunk(ctx->packet, chunk,
+                                                           one_packet, ctx->gfp);
+                       if (status != SCTP_XMIT_OK) {
                                /* put the chunk back */
-                               list_add(&chunk->list, &q->control_chunk_list);
+                               list_add(&chunk->list, &ctx->q->control_chunk_list);
                                break;
                        }
 
-                       asoc->stats.octrlchunks++;
+                       ctx->asoc->stats.octrlchunks++;
                        /* PR-SCTP C5) If a FORWARD TSN is sent, the
                         * sender MUST assure that at least one T3-rtx
                         * timer is running.
                         */
                        if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN ||
                            chunk->chunk_hdr->type == SCTP_CID_I_FWD_TSN) {
-                               sctp_transport_reset_t3_rtx(transport);
-                               transport->last_time_sent = jiffies;
+                               sctp_transport_reset_t3_rtx(ctx->transport);
+                               ctx->transport->last_time_sent = jiffies;
                        }
 
-                       if (chunk == asoc->strreset_chunk)
-                               sctp_transport_reset_reconf_timer(transport);
+                       if (chunk == ctx->asoc->strreset_chunk)
+                               sctp_transport_reset_reconf_timer(ctx->transport);
 
                        break;
 
@@ -971,232 +984,186 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
                        BUG();
                }
        }
+}
 
-       if (q->asoc->src_out_of_asoc_ok)
-               goto sctp_flush_out;
+/* Returns false if new data shouldn't be sent */
+static bool sctp_outq_flush_rtx(struct sctp_flush_ctx *ctx,
+                               int rtx_timeout)
+{
+       int error, start_timer = 0;
+
+       if (ctx->asoc->peer.retran_path->state == SCTP_UNCONFIRMED)
+               return false;
+
+       if (ctx->transport != ctx->asoc->peer.retran_path) {
+               /* Switch transports & prepare the packet.  */
+               ctx->transport = ctx->asoc->peer.retran_path;
+               ctx->packet = &ctx->transport->packet;
+
+               if (list_empty(&ctx->transport->send_ready))
+                       list_add_tail(&ctx->transport->send_ready,
+                                     &ctx->transport_list);
+
+               sctp_packet_config(ctx->packet, ctx->asoc->peer.i.init_tag,
+                                  ctx->asoc->peer.ecn_capable);
+       }
+
+       error = __sctp_outq_flush_rtx(ctx->q, ctx->packet, rtx_timeout,
+                                     &start_timer, ctx->gfp);
+       if (error < 0)
+               ctx->asoc->base.sk->sk_err = -error;
+
+       if (start_timer) {
+               sctp_transport_reset_t3_rtx(ctx->transport);
+               ctx->transport->last_time_sent = jiffies;
+       }
+
+       /* This can happen on COOKIE-ECHO resend.  Only
+        * one chunk can get bundled with a COOKIE-ECHO.
+        */
+       if (ctx->packet->has_cookie_echo)
+               return false;
+
+       /* Don't send new data if there is still data
+        * waiting to retransmit.
+        */
+       if (!list_empty(&ctx->q->retransmit))
+               return false;
+
+       return true;
+}
+
+static void sctp_outq_flush_data(struct sctp_flush_ctx *ctx,
+                                int rtx_timeout)
+{
+       struct sctp_chunk *chunk;
+       enum sctp_xmit status;
 
        /* Is it OK to send data chunks?  */
-       switch (asoc->state) {
+       switch (ctx->asoc->state) {
        case SCTP_STATE_COOKIE_ECHOED:
                /* Only allow bundling when this packet has a COOKIE-ECHO
                 * chunk.
                 */
-               if (!packet || !packet->has_cookie_echo)
-                       break;
+               if (!ctx->packet || !ctx->packet->has_cookie_echo)
+                       return;
 
                /* fallthru */
        case SCTP_STATE_ESTABLISHED:
        case SCTP_STATE_SHUTDOWN_PENDING:
        case SCTP_STATE_SHUTDOWN_RECEIVED:
-               /*
-                * RFC 2960 6.1  Transmission of DATA Chunks
-                *
-                * C) When the time comes for the sender to transmit,
-                * before sending new DATA chunks, the sender MUST
-                * first transmit any outstanding DATA chunks which
-                * are marked for retransmission (limited by the
-                * current cwnd).
-                */
-               if (!list_empty(&q->retransmit)) {
-                       if (asoc->peer.retran_path->state == SCTP_UNCONFIRMED)
-                               goto sctp_flush_out;
-                       if (transport == asoc->peer.retran_path)
-                               goto retran;
-
-                       /* Switch transports & prepare the packet.  */
-
-                       transport = asoc->peer.retran_path;
+               break;
 
-                       if (list_empty(&transport->send_ready)) {
-                               list_add_tail(&transport->send_ready,
-                                             &transport_list);
-                       }
+       default:
+               /* Do nothing. */
+               return;
+       }
 
-                       packet = &transport->packet;
-                       sctp_packet_config(packet, vtag,
-                                          asoc->peer.ecn_capable);
-               retran:
-                       error = sctp_outq_flush_rtx(q, packet,
-                                                   rtx_timeout, &start_timer);
-                       if (error < 0)
-                               asoc->base.sk->sk_err = -error;
+       /* RFC 2960 6.1  Transmission of DATA Chunks
+        *
+        * C) When the time comes for the sender to transmit,
+        * before sending new DATA chunks, the sender MUST
+        * first transmit any outstanding DATA chunks which
+        * are marked for retransmission (limited by the
+        * current cwnd).
+        */
+       if (!list_empty(&ctx->q->retransmit) &&
+           !sctp_outq_flush_rtx(ctx, rtx_timeout))
+               return;
 
-                       if (start_timer) {
-                               sctp_transport_reset_t3_rtx(transport);
-                               transport->last_time_sent = jiffies;
-                       }
+       /* Apply Max.Burst limitation to the current transport in
+        * case it will be used for new data.  We are going to
+        * rest it before we return, but we want to apply the limit
+        * to the currently queued data.
+        */
+       if (ctx->transport)
+               sctp_transport_burst_limited(ctx->transport);
 
-                       /* This can happen on COOKIE-ECHO resend.  Only
-                        * one chunk can get bundled with a COOKIE-ECHO.
-                        */
-                       if (packet->has_cookie_echo)
-                               goto sctp_flush_out;
+       /* Finally, transmit new packets.  */
+       while ((chunk = sctp_outq_dequeue_data(ctx->q)) != NULL) {
+               __u32 sid = ntohs(chunk->subh.data_hdr->stream);
 
-                       /* Don't send new data if there is still data
-                        * waiting to retransmit.
-                        */
-                       if (!list_empty(&q->retransmit))
-                               goto sctp_flush_out;
+               /* Has this chunk expired? */
+               if (sctp_chunk_abandoned(chunk)) {
+                       sctp_sched_dequeue_done(ctx->q, chunk);
+                       sctp_chunk_fail(chunk, 0);
+                       sctp_chunk_free(chunk);
+                       continue;
                }
 
-               /* Apply Max.Burst limitation to the current transport in
-                * case it will be used for new data.  We are going to
-                * rest it before we return, but we want to apply the limit
-                * to the currently queued data.
-                */
-               if (transport)
-                       sctp_transport_burst_limited(transport);
-
-               /* Finally, transmit new packets.  */
-               while ((chunk = sctp_outq_dequeue_data(q)) != NULL) {
-                       __u32 sid = ntohs(chunk->subh.data_hdr->stream);
-
-                       /* Has this chunk expired? */
-                       if (sctp_chunk_abandoned(chunk)) {
-                               sctp_sched_dequeue_done(q, chunk);
-                               sctp_chunk_fail(chunk, 0);
-                               sctp_chunk_free(chunk);
-                               continue;
-                       }
+               if (ctx->asoc->stream.out[sid].state == SCTP_STREAM_CLOSED) {
+                       sctp_outq_head_data(ctx->q, chunk);
+                       break;
+               }
 
-                       if (asoc->stream.out[sid].state == SCTP_STREAM_CLOSED) {
-                               sctp_outq_head_data(q, chunk);
-                               goto sctp_flush_out;
-                       }
+               sctp_outq_select_transport(ctx, chunk);
 
-                       /* If there is a specified transport, use it.
-                        * Otherwise, we want to use the active path.
+               pr_debug("%s: outq:%p, chunk:%p[%s], tx-tsn:0x%x skb->head:%p skb->users:%d\n",
+                        __func__, ctx->q, chunk, chunk && chunk->chunk_hdr ?
+                        sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
+                        "illegal chunk", ntohl(chunk->subh.data_hdr->tsn),
+                        chunk->skb ? chunk->skb->head : NULL, chunk->skb ?
+                        refcount_read(&chunk->skb->users) : -1);
+
+               /* Add the chunk to the packet.  */
+               status = sctp_packet_transmit_chunk(ctx->packet, chunk, 0,
+                                                   ctx->gfp);
+               if (status != SCTP_XMIT_OK) {
+                       /* We could not append this chunk, so put
+                        * the chunk back on the output queue.
                         */
-                       new_transport = chunk->transport;
-                       if (!new_transport ||
-                           ((new_transport->state == SCTP_INACTIVE) ||
-                            (new_transport->state == SCTP_UNCONFIRMED) ||
-                            (new_transport->state == SCTP_PF)))
-                               new_transport = asoc->peer.active_path;
-                       if (new_transport->state == SCTP_UNCONFIRMED) {
-                               WARN_ONCE(1, "Attempt to send packet on unconfirmed path.");
-                               sctp_sched_dequeue_done(q, chunk);
-                               sctp_chunk_fail(chunk, 0);
-                               sctp_chunk_free(chunk);
-                               continue;
-                       }
-
-                       /* Change packets if necessary.  */
-                       if (new_transport != transport) {
-                               transport = new_transport;
-
-                               /* Schedule to have this transport's
-                                * packet flushed.
-                                */
-                               if (list_empty(&transport->send_ready)) {
-                                       list_add_tail(&transport->send_ready,
-                                                     &transport_list);
-                               }
+                       pr_debug("%s: could not transmit tsn:0x%x, status:%d\n",
+                                __func__, ntohl(chunk->subh.data_hdr->tsn),
+                                status);
 
-                               packet = &transport->packet;
-                               sctp_packet_config(packet, vtag,
-                                                  asoc->peer.ecn_capable);
-                               /* We've switched transports, so apply the
-                                * Burst limit to the new transport.
-                                */
-                               sctp_transport_burst_limited(transport);
-                       }
-
-                       pr_debug("%s: outq:%p, chunk:%p[%s], tx-tsn:0x%x skb->head:%p "
-                                "skb->users:%d\n",
-                                __func__, q, chunk, chunk && chunk->chunk_hdr ?
-                                sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
-                                "illegal chunk", ntohl(chunk->subh.data_hdr->tsn),
-                                chunk->skb ? chunk->skb->head : NULL, chunk->skb ?
-                                refcount_read(&chunk->skb->users) : -1);
-
-                       /* Add the chunk to the packet.  */
-                       status = sctp_packet_transmit_chunk(packet, chunk, 0, gfp);
-
-                       switch (status) {
-                       case SCTP_XMIT_PMTU_FULL:
-                       case SCTP_XMIT_RWND_FULL:
-                       case SCTP_XMIT_DELAY:
-                               /* We could not append this chunk, so put
-                                * the chunk back on the output queue.
-                                */
-                               pr_debug("%s: could not transmit tsn:0x%x, status:%d\n",
-                                        __func__, ntohl(chunk->subh.data_hdr->tsn),
-                                        status);
-
-                               sctp_outq_head_data(q, chunk);
-                               goto sctp_flush_out;
-
-                       case SCTP_XMIT_OK:
-                               /* The sender is in the SHUTDOWN-PENDING state,
-                                * The sender MAY set the I-bit in the DATA
-                                * chunk header.
-                                */
-                               if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING)
-                                       chunk->chunk_hdr->flags |= SCTP_DATA_SACK_IMM;
-                               if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
-                                       asoc->stats.ouodchunks++;
-                               else
-                                       asoc->stats.oodchunks++;
-
-                               /* Only now it's safe to consider this
-                                * chunk as sent, sched-wise.
-                                */
-                               sctp_sched_dequeue_done(q, chunk);
-
-                               break;
+                       sctp_outq_head_data(ctx->q, chunk);
+                       break;
+               }
 
-                       default:
-                               BUG();
-                       }
+               /* The sender is in the SHUTDOWN-PENDING state,
+                * The sender MAY set the I-bit in the DATA
+                * chunk header.
+                */
+               if (ctx->asoc->state == SCTP_STATE_SHUTDOWN_PENDING)
+                       chunk->chunk_hdr->flags |= SCTP_DATA_SACK_IMM;
+               if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
+                       ctx->asoc->stats.ouodchunks++;
+               else
+                       ctx->asoc->stats.oodchunks++;
 
-                       /* BUG: We assume that the sctp_packet_transmit()
-                        * call below will succeed all the time and add the
-                        * chunk to the transmitted list and restart the
-                        * timers.
-                        * It is possible that the call can fail under OOM
-                        * conditions.
-                        *
-                        * Is this really a problem?  Won't this behave
-                        * like a lost TSN?
-                        */
-                       list_add_tail(&chunk->transmitted_list,
-                                     &transport->transmitted);
+               /* Only now it's safe to consider this
+                * chunk as sent, sched-wise.
+                */
+               sctp_sched_dequeue_done(ctx->q, chunk);
 
-                       sctp_transport_reset_t3_rtx(transport);
-                       transport->last_time_sent = jiffies;
+               list_add_tail(&chunk->transmitted_list,
+                             &ctx->transport->transmitted);
 
-                       /* Only let one DATA chunk get bundled with a
-                        * COOKIE-ECHO chunk.
-                        */
-                       if (packet->has_cookie_echo)
-                               goto sctp_flush_out;
-               }
-               break;
+               sctp_transport_reset_t3_rtx(ctx->transport);
+               ctx->transport->last_time_sent = jiffies;
 
-       default:
-               /* Do nothing.  */
-               break;
+               /* Only let one DATA chunk get bundled with a
+                * COOKIE-ECHO chunk.
+                */
+               if (ctx->packet->has_cookie_echo)
+                       break;
        }
+}
 
-sctp_flush_out:
+static void sctp_outq_flush_transports(struct sctp_flush_ctx *ctx)
+{
+       struct list_head *ltransport;
+       struct sctp_packet *packet;
+       struct sctp_transport *t;
+       int error = 0;
 
-       /* Before returning, examine all the transports touched in
-        * this call.  Right now, we bluntly force clear all the
-        * transports.  Things might change after we implement Nagle.
-        * But such an examination is still required.
-        *
-        * --xguo
-        */
-       while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL) {
-               struct sctp_transport *t = list_entry(ltransport,
-                                                     struct sctp_transport,
-                                                     send_ready);
+       while ((ltransport = sctp_list_dequeue(&ctx->transport_list)) != NULL) {
+               t = list_entry(ltransport, struct sctp_transport, send_ready);
                packet = &t->packet;
                if (!sctp_packet_empty(packet)) {
-                       error = sctp_packet_transmit(packet, gfp);
+                       error = sctp_packet_transmit(packet, ctx->gfp);
                        if (error < 0)
-                               asoc->base.sk->sk_err = -error;
+                               ctx->q->asoc->base.sk->sk_err = -error;
                }
 
                /* Clear the burst limited state, if any */
@@ -1204,6 +1171,47 @@ sctp_flush_out:
        }
 }
 
+/* Try to flush an outqueue.
+ *
+ * Description: Send everything in q which we legally can, subject to
+ * congestion limitations.
+ * * Note: This function can be called from multiple contexts so appropriate
+ * locking concerns must be made.  Today we use the sock lock to protect
+ * this function.
+ */
+
+static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
+{
+       struct sctp_flush_ctx ctx = {
+               .q = q,
+               .transport = NULL,
+               .transport_list = LIST_HEAD_INIT(ctx.transport_list),
+               .asoc = q->asoc,
+               .packet = NULL,
+               .gfp = gfp,
+       };
+
+       /* 6.10 Bundling
+        *   ...
+        *   When bundling control chunks with DATA chunks, an
+        *   endpoint MUST place control chunks first in the outbound
+        *   SCTP packet.  The transmitter MUST transmit DATA chunks
+        *   within a SCTP packet in increasing order of TSN.
+        *   ...
+        */
+
+       sctp_outq_flush_ctrl(&ctx);
+
+       if (q->asoc->src_out_of_asoc_ok)
+               goto sctp_flush_out;
+
+       sctp_outq_flush_data(&ctx, rtx_timeout);
+
+sctp_flush_out:
+
+       sctp_outq_flush_transports(&ctx);
+}
+
 /* Update unack_data based on the incoming SACK chunk */
 static void sctp_sack_update_unack_data(struct sctp_association *assoc,
                                        struct sctp_sackhdr *sack)
@@ -1754,7 +1762,7 @@ static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn)
        if (TSN_lte(tsn, ctsn))
                goto pass;
 
-       /* 3.3.4 Selective Acknowledgement (SACK) (3):
+       /* 3.3.4 Selective Acknowledgment (SACK) (3):
         *
         * Gap Ack Blocks:
         *  These fields contain the Gap Ack Blocks. They are repeated
index 17688a0..2c369d4 100644 (file)
@@ -8,8 +8,6 @@
  *
  *  Initial restrictions:
  *    - support for alternate links postponed
- *    - partial support for non-blocking sockets only
- *    - support for urgent data postponed
  *
  *  Copyright IBM Corp. 2016, 2018
  *
@@ -46,11 +44,6 @@ static DEFINE_MUTEX(smc_create_lgr_pending); /* serialize link group
                                                 * creation
                                                 */
 
-struct smc_lgr_list smc_lgr_list = {           /* established link groups */
-       .lock = __SPIN_LOCK_UNLOCKED(smc_lgr_list.lock),
-       .list = LIST_HEAD_INIT(smc_lgr_list.list),
-};
-
 static void smc_tcp_listen_work(struct work_struct *);
 
 static void smc_set_keepalive(struct sock *sk, int val)
@@ -193,8 +186,10 @@ static struct sock *smc_sock_alloc(struct net *net, struct socket *sock,
        sk->sk_protocol = protocol;
        smc = smc_sk(sk);
        INIT_WORK(&smc->tcp_listen_work, smc_tcp_listen_work);
+       INIT_DELAYED_WORK(&smc->conn.tx_work, smc_tx_work);
        INIT_LIST_HEAD(&smc->accept_q);
        spin_lock_init(&smc->accept_q_lock);
+       spin_lock_init(&smc->conn.send_lock);
        sk->sk_prot->hash(sk);
        sk_refcnt_debug_inc(sk);
 
@@ -293,14 +288,22 @@ 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 */
-static int smc_reg_rmb(struct smc_link *link, struct smc_buf_desc *rmb_desc)
+/* register a new rmb, optionally 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 (!conf_rkey)
+               return 0;
+       /* exchange confirm_rkey msg with peer */
+       if (smc_llc_do_confirm_rkey(link, rmb_desc)) {
+               rmb_desc->regerr = 1;
+               return -EFAULT;
+       }
        return 0;
 }
 
@@ -334,7 +337,7 @@ static int smc_clnt_conf_first_link(struct smc_sock *smc)
 
        smc_wr_remember_qp_attr(link);
 
-       if (smc_reg_rmb(link, smc->conn.rmb_desc))
+       if (smc_reg_rmb(link, smc->conn.rmb_desc, false))
                return SMC_CLC_DECL_INTERR;
 
        /* send CONFIRM LINK response over RoCE fabric */
@@ -372,10 +375,13 @@ static int smc_clnt_conf_first_link(struct smc_sock *smc)
 static void smc_conn_save_peer_info(struct smc_sock *smc,
                                    struct smc_clc_msg_accept_confirm *clc)
 {
-       smc->conn.peer_conn_idx = clc->conn_idx;
+       int bufsize = smc_uncompress_bufsize(clc->rmbe_size);
+
+       smc->conn.peer_rmbe_idx = clc->rmbe_idx;
        smc->conn.local_tx_ctrl.token = ntohl(clc->rmbe_alert_token);
-       smc->conn.peer_rmbe_size = smc_uncompress_bufsize(clc->rmbe_size);
+       smc->conn.peer_rmbe_size = bufsize;
        atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size);
+       smc->conn.tx_off = bufsize * (smc->conn.peer_rmbe_idx - 1);
 }
 
 static void smc_link_save_peer_info(struct smc_link *link,
@@ -388,163 +394,186 @@ static void smc_link_save_peer_info(struct smc_link *link,
        link->peer_mtu = clc->qp_mtu;
 }
 
-/* setup for RDMA connection of client */
-static int smc_connect_rdma(struct smc_sock *smc)
+/* fall back during connect */
+static int smc_connect_fallback(struct smc_sock *smc)
 {
-       struct smc_clc_msg_accept_confirm aclc;
-       int local_contact = SMC_FIRST_CONTACT;
-       struct smc_ib_device *smcibdev;
-       struct smc_link *link;
-       u8 srv_first_contact;
-       int reason_code = 0;
-       int rc = 0;
-       u8 ibport;
-
-       sock_hold(&smc->sk); /* sock put in passive closing */
+       smc->use_fallback = true;
+       smc_copy_sock_settings_to_clc(smc);
+       if (smc->sk.sk_state == SMC_INIT)
+               smc->sk.sk_state = SMC_ACTIVE;
+       return 0;
+}
 
-       if (smc->use_fallback)
-               goto out_connected;
+/* decline and fall back during connect */
+static int smc_connect_decline_fallback(struct smc_sock *smc, int reason_code)
+{
+       int rc;
 
-       if (!tcp_sk(smc->clcsock->sk)->syn_smc) {
-               /* peer has not signalled SMC-capability */
-               smc->use_fallback = true;
-               goto out_connected;
+       if (reason_code < 0) /* error, fallback is not possible */
+               return reason_code;
+       if (reason_code != SMC_CLC_DECL_REPLY) {
+               rc = smc_clc_send_decline(smc, reason_code);
+               if (rc < 0)
+                       return rc;
        }
+       return smc_connect_fallback(smc);
+}
 
-       /* IPSec connections opt out of SMC-R optimizations */
-       if (using_ipsec(smc)) {
-               reason_code = SMC_CLC_DECL_IPSEC;
-               goto decline_rdma;
-       }
+/* abort connecting */
+static int smc_connect_abort(struct smc_sock *smc, int reason_code,
+                            int local_contact)
+{
+       if (local_contact == SMC_FIRST_CONTACT)
+               smc_lgr_forget(smc->conn.lgr);
+       mutex_unlock(&smc_create_lgr_pending);
+       smc_conn_free(&smc->conn);
+       if (reason_code < 0 && smc->sk.sk_state == SMC_INIT)
+               sock_put(&smc->sk); /* passive closing */
+       return reason_code;
+}
+
+/* check if there is a rdma device available for this connection. */
+/* called for connect and listen */
+static int smc_check_rdma(struct smc_sock *smc, struct smc_ib_device **ibdev,
+                         u8 *ibport)
+{
+       int reason_code = 0;
 
        /* PNET table look up: search active ib_device and port
         * within same PNETID that also contains the ethernet device
         * used for the internal TCP socket
         */
-       smc_pnet_find_roce_resource(smc->clcsock->sk, &smcibdev, &ibport);
-       if (!smcibdev) {
+       smc_pnet_find_roce_resource(smc->clcsock->sk, ibdev, ibport);
+       if (!(*ibdev))
                reason_code = SMC_CLC_DECL_CNFERR; /* configuration error */
-               goto decline_rdma;
-       }
+
+       return reason_code;
+}
+
+/* CLC handshake during connect */
+static int smc_connect_clc(struct smc_sock *smc,
+                          struct smc_clc_msg_accept_confirm *aclc,
+                          struct smc_ib_device *ibdev, u8 ibport)
+{
+       int rc = 0;
 
        /* do inband token exchange */
-       reason_code = smc_clc_send_proposal(smc, smcibdev, ibport);
-       if (reason_code < 0) {
-               rc = reason_code;
-               goto out_err;
-       }
-       if (reason_code > 0) /* configuration error */
-               goto decline_rdma;
+       rc = smc_clc_send_proposal(smc, ibdev, ibport);
+       if (rc)
+               return rc;
        /* receive SMC Accept CLC message */
-       reason_code = smc_clc_wait_msg(smc, &aclc, sizeof(aclc),
-                                      SMC_CLC_ACCEPT);
-       if (reason_code < 0) {
-               rc = reason_code;
-               goto out_err;
-       }
-       if (reason_code > 0)
-               goto decline_rdma;
+       return smc_clc_wait_msg(smc, aclc, sizeof(*aclc), SMC_CLC_ACCEPT);
+}
+
+/* setup for RDMA connection of client */
+static int smc_connect_rdma(struct smc_sock *smc,
+                           struct smc_clc_msg_accept_confirm *aclc,
+                           struct smc_ib_device *ibdev, u8 ibport)
+{
+       int local_contact = SMC_FIRST_CONTACT;
+       struct smc_link *link;
+       int reason_code = 0;
 
-       srv_first_contact = aclc.hdr.flag;
        mutex_lock(&smc_create_lgr_pending);
-       local_contact = smc_conn_create(smc, smcibdev, ibport, &aclc.lcl,
-                                       srv_first_contact);
+       local_contact = smc_conn_create(smc, ibdev, ibport, &aclc->lcl,
+                                       aclc->hdr.flag);
        if (local_contact < 0) {
-               rc = local_contact;
-               if (rc == -ENOMEM)
+               if (local_contact == -ENOMEM)
                        reason_code = SMC_CLC_DECL_MEM;/* insufficient memory*/
-               else if (rc == -ENOLINK)
+               else if (local_contact == -ENOLINK)
                        reason_code = SMC_CLC_DECL_SYNCERR; /* synchr. error */
-               goto decline_rdma_unlock;
+               else
+                       reason_code = SMC_CLC_DECL_INTERR; /* other error */
+               return smc_connect_abort(smc, reason_code, 0);
        }
        link = &smc->conn.lgr->lnk[SMC_SINGLE_LINK];
 
-       smc_conn_save_peer_info(smc, &aclc);
+       smc_conn_save_peer_info(smc, aclc);
 
        /* create send buffer and rmb */
-       rc = smc_buf_create(smc);
-       if (rc) {
-               reason_code = SMC_CLC_DECL_MEM;
-               goto decline_rdma_unlock;
-       }
+       if (smc_buf_create(smc))
+               return smc_connect_abort(smc, SMC_CLC_DECL_MEM, local_contact);
 
        if (local_contact == SMC_FIRST_CONTACT)
-               smc_link_save_peer_info(link, &aclc);
+               smc_link_save_peer_info(link, aclc);
 
-       rc = smc_rmb_rtoken_handling(&smc->conn, &aclc);
-       if (rc) {
-               reason_code = SMC_CLC_DECL_INTERR;
-               goto decline_rdma_unlock;
-       }
+       if (smc_rmb_rtoken_handling(&smc->conn, aclc))
+               return smc_connect_abort(smc, SMC_CLC_DECL_INTERR,
+                                        local_contact);
 
        smc_close_init(smc);
        smc_rx_init(smc);
 
        if (local_contact == SMC_FIRST_CONTACT) {
-               rc = smc_ib_ready_link(link);
-               if (rc) {
-                       reason_code = SMC_CLC_DECL_INTERR;
-                       goto decline_rdma_unlock;
-               }
+               if (smc_ib_ready_link(link))
+                       return smc_connect_abort(smc, SMC_CLC_DECL_INTERR,
+                                                local_contact);
        } else {
-               if (!smc->conn.rmb_desc->reused) {
-                       if (smc_reg_rmb(link, smc->conn.rmb_desc)) {
-                               reason_code = SMC_CLC_DECL_INTERR;
-                               goto decline_rdma_unlock;
-                       }
-               }
+               if (!smc->conn.rmb_desc->reused &&
+                   smc_reg_rmb(link, smc->conn.rmb_desc, true))
+                       return smc_connect_abort(smc, SMC_CLC_DECL_INTERR,
+                                                local_contact);
        }
        smc_rmb_sync_sg_for_device(&smc->conn);
 
-       rc = smc_clc_send_confirm(smc);
-       if (rc)
-               goto out_err_unlock;
+       reason_code = smc_clc_send_confirm(smc);
+       if (reason_code)
+               return smc_connect_abort(smc, reason_code, local_contact);
+
+       smc_tx_init(smc);
 
        if (local_contact == SMC_FIRST_CONTACT) {
                /* QP confirmation over RoCE fabric */
                reason_code = smc_clnt_conf_first_link(smc);
-               if (reason_code < 0) {
-                       rc = reason_code;
-                       goto out_err_unlock;
-               }
-               if (reason_code > 0)
-                       goto decline_rdma_unlock;
+               if (reason_code)
+                       return smc_connect_abort(smc, reason_code,
+                                                local_contact);
        }
-
        mutex_unlock(&smc_create_lgr_pending);
-       smc_tx_init(smc);
 
-out_connected:
        smc_copy_sock_settings_to_clc(smc);
        if (smc->sk.sk_state == SMC_INIT)
                smc->sk.sk_state = SMC_ACTIVE;
 
-       return rc ? rc : local_contact;
+       return 0;
+}
+
+/* perform steps before actually connecting */
+static int __smc_connect(struct smc_sock *smc)
+{
+       struct smc_clc_msg_accept_confirm aclc;
+       struct smc_ib_device *ibdev;
+       int rc = 0;
+       u8 ibport;
 
-decline_rdma_unlock:
-       if (local_contact == SMC_FIRST_CONTACT)
-               smc_lgr_forget(smc->conn.lgr);
-       mutex_unlock(&smc_create_lgr_pending);
-       smc_conn_free(&smc->conn);
-decline_rdma:
-       /* RDMA setup failed, switch back to TCP */
-       smc->use_fallback = true;
-       if (reason_code && (reason_code != SMC_CLC_DECL_REPLY)) {
-               rc = smc_clc_send_decline(smc, reason_code);
-               if (rc < 0)
-                       goto out_err;
-       }
-       goto out_connected;
+       sock_hold(&smc->sk); /* sock put in passive closing */
 
-out_err_unlock:
-       if (local_contact == SMC_FIRST_CONTACT)
-               smc_lgr_forget(smc->conn.lgr);
-       mutex_unlock(&smc_create_lgr_pending);
-       smc_conn_free(&smc->conn);
-out_err:
-       if (smc->sk.sk_state == SMC_INIT)
-               sock_put(&smc->sk); /* passive closing */
-       return rc;
+       if (smc->use_fallback)
+               return smc_connect_fallback(smc);
+
+       /* if peer has not signalled SMC-capability, fall back */
+       if (!tcp_sk(smc->clcsock->sk)->syn_smc)
+               return smc_connect_fallback(smc);
+
+       /* IPSec connections opt out of SMC-R optimizations */
+       if (using_ipsec(smc))
+               return smc_connect_decline_fallback(smc, SMC_CLC_DECL_IPSEC);
+
+       /* check if a RDMA device is available; if not, fall back */
+       if (smc_check_rdma(smc, &ibdev, &ibport))
+               return smc_connect_decline_fallback(smc, SMC_CLC_DECL_CNFERR);
+
+       /* perform CLC handshake */
+       rc = smc_connect_clc(smc, &aclc, ibdev, ibport);
+       if (rc)
+               return smc_connect_decline_fallback(smc, rc);
+
+       /* connect using rdma */
+       rc = smc_connect_rdma(smc, &aclc, ibdev, ibport);
+       if (rc)
+               return smc_connect_decline_fallback(smc, rc);
+
+       return 0;
 }
 
 static int smc_connect(struct socket *sock, struct sockaddr *addr,
@@ -580,8 +609,7 @@ static int smc_connect(struct socket *sock, struct sockaddr *addr,
        if (rc)
                goto out;
 
-       /* setup RDMA connection */
-       rc = smc_connect_rdma(smc);
+       rc = __smc_connect(smc);
        if (rc < 0)
                goto out;
        else
@@ -729,7 +757,7 @@ static int smc_serv_conf_first_link(struct smc_sock *smc)
 
        link = &lgr->lnk[SMC_SINGLE_LINK];
 
-       if (smc_reg_rmb(link, smc->conn.rmb_desc))
+       if (smc_reg_rmb(link, smc->conn.rmb_desc, false))
                return SMC_CLC_DECL_INTERR;
 
        /* send CONFIRM LINK request to client over the RoCE fabric */
@@ -779,182 +807,239 @@ static int smc_serv_conf_first_link(struct smc_sock *smc)
        return 0;
 }
 
-/* setup for RDMA connection of server */
-static void smc_listen_work(struct work_struct *work)
+/* listen worker: finish */
+static void smc_listen_out(struct smc_sock *new_smc)
 {
-       struct smc_sock *new_smc = container_of(work, struct smc_sock,
-                                               smc_listen_work);
-       struct smc_clc_msg_proposal_prefix *pclc_prfx;
-       struct socket *newclcsock = new_smc->clcsock;
        struct smc_sock *lsmc = new_smc->listen_smc;
-       struct smc_clc_msg_accept_confirm cclc;
-       int local_contact = SMC_REUSE_CONTACT;
        struct sock *newsmcsk = &new_smc->sk;
-       struct smc_clc_msg_proposal *pclc;
-       struct smc_ib_device *smcibdev;
-       u8 buf[SMC_CLC_MAX_LEN];
-       struct smc_link *link;
-       int reason_code = 0;
-       int rc = 0;
-       u8 ibport;
 
-       if (new_smc->use_fallback)
-               goto out_connected;
-
-       /* check if peer is smc capable */
-       if (!tcp_sk(newclcsock->sk)->syn_smc) {
-               new_smc->use_fallback = true;
-               goto out_connected;
+       lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING);
+       if (lsmc->sk.sk_state == SMC_LISTEN) {
+               smc_accept_enqueue(&lsmc->sk, newsmcsk);
+       } else { /* no longer listening */
+               smc_close_non_accepted(newsmcsk);
        }
+       release_sock(&lsmc->sk);
 
-       /* do inband token exchange -
-        *wait for and receive SMC Proposal CLC message
-        */
-       reason_code = smc_clc_wait_msg(new_smc, &buf, sizeof(buf),
-                                      SMC_CLC_PROPOSAL);
-       if (reason_code < 0)
-               goto out_err;
-       if (reason_code > 0)
-               goto decline_rdma;
+       /* Wake up accept */
+       lsmc->sk.sk_data_ready(&lsmc->sk);
+       sock_put(&lsmc->sk); /* sock_hold in smc_tcp_listen_work */
+}
 
-       /* IPSec connections opt out of SMC-R optimizations */
-       if (using_ipsec(new_smc)) {
-               reason_code = SMC_CLC_DECL_IPSEC;
-               goto decline_rdma;
-       }
+/* listen worker: finish in state connected */
+static void smc_listen_out_connected(struct smc_sock *new_smc)
+{
+       struct sock *newsmcsk = &new_smc->sk;
 
-       /* PNET table look up: search active ib_device and port
-        * within same PNETID that also contains the ethernet device
-        * used for the internal TCP socket
-        */
-       smc_pnet_find_roce_resource(newclcsock->sk, &smcibdev, &ibport);
-       if (!smcibdev) {
-               reason_code = SMC_CLC_DECL_CNFERR; /* configuration error */
-               goto decline_rdma;
+       sk_refcnt_debug_inc(newsmcsk);
+       if (newsmcsk->sk_state == SMC_INIT)
+               newsmcsk->sk_state = SMC_ACTIVE;
+
+       smc_listen_out(new_smc);
+}
+
+/* listen worker: finish in error state */
+static void smc_listen_out_err(struct smc_sock *new_smc)
+{
+       struct sock *newsmcsk = &new_smc->sk;
+
+       if (newsmcsk->sk_state == SMC_INIT)
+               sock_put(&new_smc->sk); /* passive closing */
+       newsmcsk->sk_state = SMC_CLOSED;
+       smc_conn_free(&new_smc->conn);
+
+       smc_listen_out(new_smc);
+}
+
+/* listen worker: decline and fall back if possible */
+static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
+                              int local_contact)
+{
+       /* RDMA setup failed, switch back to TCP */
+       if (local_contact == SMC_FIRST_CONTACT)
+               smc_lgr_forget(new_smc->conn.lgr);
+       if (reason_code < 0) { /* error, no fallback possible */
+               smc_listen_out_err(new_smc);
+               return;
+       }
+       smc_conn_free(&new_smc->conn);
+       new_smc->use_fallback = true;
+       if (reason_code && reason_code != SMC_CLC_DECL_REPLY) {
+               if (smc_clc_send_decline(new_smc, reason_code) < 0) {
+                       smc_listen_out_err(new_smc);
+                       return;
+               }
        }
+       smc_listen_out_connected(new_smc);
+}
+
+/* listen worker: check prefixes */
+static int smc_listen_rdma_check(struct smc_sock *new_smc,
+                                struct smc_clc_msg_proposal *pclc)
+{
+       struct smc_clc_msg_proposal_prefix *pclc_prfx;
+       struct socket *newclcsock = new_smc->clcsock;
 
-       pclc = (struct smc_clc_msg_proposal *)&buf;
        pclc_prfx = smc_clc_proposal_get_prefix(pclc);
+       if (smc_clc_prfx_match(newclcsock, pclc_prfx))
+               return SMC_CLC_DECL_CNFERR;
 
-       rc = smc_clc_prfx_match(newclcsock, pclc_prfx);
-       if (rc) {
-               reason_code = SMC_CLC_DECL_CNFERR; /* configuration error */
-               goto decline_rdma;
-       }
+       return 0;
+}
 
+/* listen worker: initialize connection and buffers */
+static int smc_listen_rdma_init(struct smc_sock *new_smc,
+                               struct smc_clc_msg_proposal *pclc,
+                               struct smc_ib_device *ibdev, u8 ibport,
+                               int *local_contact)
+{
        /* allocate connection / link group */
-       mutex_lock(&smc_create_lgr_pending);
-       local_contact = smc_conn_create(new_smc, smcibdev, ibport, &pclc->lcl,
-                                       0);
-       if (local_contact < 0) {
-               rc = local_contact;
-               if (rc == -ENOMEM)
-                       reason_code = SMC_CLC_DECL_MEM;/* insufficient memory*/
-               goto decline_rdma_unlock;
+       *local_contact = smc_conn_create(new_smc, ibdev, ibport, &pclc->lcl, 0);
+       if (*local_contact < 0) {
+               if (*local_contact == -ENOMEM)
+                       return SMC_CLC_DECL_MEM;/* insufficient memory*/
+               return SMC_CLC_DECL_INTERR; /* other error */
        }
-       link = &new_smc->conn.lgr->lnk[SMC_SINGLE_LINK];
 
        /* create send buffer and rmb */
-       rc = smc_buf_create(new_smc);
-       if (rc) {
-               reason_code = SMC_CLC_DECL_MEM;
-               goto decline_rdma_unlock;
-       }
+       if (smc_buf_create(new_smc))
+               return SMC_CLC_DECL_MEM;
 
-       smc_close_init(new_smc);
-       smc_rx_init(new_smc);
+       return 0;
+}
+
+/* listen worker: register buffers */
+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)) {
-                               reason_code = SMC_CLC_DECL_INTERR;
-                               goto decline_rdma_unlock;
-                       }
+                       if (smc_reg_rmb(link, new_smc->conn.rmb_desc, true))
+                               return SMC_CLC_DECL_INTERR;
                }
        }
        smc_rmb_sync_sg_for_device(&new_smc->conn);
 
-       rc = smc_clc_send_accept(new_smc, local_contact);
-       if (rc)
-               goto out_err_unlock;
+       return 0;
+}
+
+/* listen worker: finish RDMA setup */
+static void smc_listen_rdma_finish(struct smc_sock *new_smc,
+                                  struct smc_clc_msg_accept_confirm *cclc,
+                                  int local_contact)
+{
+       struct smc_link *link = &new_smc->conn.lgr->lnk[SMC_SINGLE_LINK];
+       int reason_code = 0;
 
-       /* receive SMC Confirm CLC message */
-       reason_code = smc_clc_wait_msg(new_smc, &cclc, sizeof(cclc),
-                                      SMC_CLC_CONFIRM);
-       if (reason_code < 0)
-               goto out_err_unlock;
-       if (reason_code > 0)
-               goto decline_rdma_unlock;
-       smc_conn_save_peer_info(new_smc, &cclc);
        if (local_contact == SMC_FIRST_CONTACT)
-               smc_link_save_peer_info(link, &cclc);
+               smc_link_save_peer_info(link, cclc);
 
-       rc = smc_rmb_rtoken_handling(&new_smc->conn, &cclc);
-       if (rc) {
+       if (smc_rmb_rtoken_handling(&new_smc->conn, cclc)) {
                reason_code = SMC_CLC_DECL_INTERR;
-               goto decline_rdma_unlock;
+               goto decline;
        }
 
        if (local_contact == SMC_FIRST_CONTACT) {
-               rc = smc_ib_ready_link(link);
-               if (rc) {
+               if (smc_ib_ready_link(link)) {
                        reason_code = SMC_CLC_DECL_INTERR;
-                       goto decline_rdma_unlock;
+                       goto decline;
                }
                /* QP confirmation over RoCE fabric */
                reason_code = smc_serv_conf_first_link(new_smc);
-               if (reason_code < 0)
-                       /* peer is not aware of a problem */
-                       goto out_err_unlock;
-               if (reason_code > 0)
-                       goto decline_rdma_unlock;
+               if (reason_code)
+                       goto decline;
        }
+       return;
 
-       smc_tx_init(new_smc);
+decline:
        mutex_unlock(&smc_create_lgr_pending);
+       smc_listen_decline(new_smc, reason_code, local_contact);
+}
 
-out_connected:
-       sk_refcnt_debug_inc(newsmcsk);
-       if (newsmcsk->sk_state == SMC_INIT)
-               newsmcsk->sk_state = SMC_ACTIVE;
-enqueue:
-       lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING);
-       if (lsmc->sk.sk_state == SMC_LISTEN) {
-               smc_accept_enqueue(&lsmc->sk, newsmcsk);
-       } else { /* no longer listening */
-               smc_close_non_accepted(newsmcsk);
+/* setup for RDMA connection of server */
+static void smc_listen_work(struct work_struct *work)
+{
+       struct smc_sock *new_smc = container_of(work, struct smc_sock,
+                                               smc_listen_work);
+       struct socket *newclcsock = new_smc->clcsock;
+       struct smc_clc_msg_accept_confirm cclc;
+       struct smc_clc_msg_proposal *pclc;
+       struct smc_ib_device *ibdev;
+       u8 buf[SMC_CLC_MAX_LEN];
+       int local_contact = 0;
+       int reason_code = 0;
+       int rc = 0;
+       u8 ibport;
+
+       if (new_smc->use_fallback) {
+               smc_listen_out_connected(new_smc);
+               return;
        }
-       release_sock(&lsmc->sk);
 
-       /* Wake up accept */
-       lsmc->sk.sk_data_ready(&lsmc->sk);
-       sock_put(&lsmc->sk); /* sock_hold in smc_tcp_listen_work */
-       return;
+       /* check if peer is smc capable */
+       if (!tcp_sk(newclcsock->sk)->syn_smc) {
+               new_smc->use_fallback = true;
+               smc_listen_out_connected(new_smc);
+               return;
+       }
 
-decline_rdma_unlock:
-       if (local_contact == SMC_FIRST_CONTACT)
-               smc_lgr_forget(new_smc->conn.lgr);
-       mutex_unlock(&smc_create_lgr_pending);
-decline_rdma:
-       /* RDMA setup failed, switch back to TCP */
-       smc_conn_free(&new_smc->conn);
-       new_smc->use_fallback = true;
-       if (reason_code && (reason_code != SMC_CLC_DECL_REPLY)) {
-               if (smc_clc_send_decline(new_smc, reason_code) < 0)
-                       goto out_err;
+       /* do inband token exchange -
+        * wait for and receive SMC Proposal CLC message
+        */
+       pclc = (struct smc_clc_msg_proposal *)&buf;
+       reason_code = smc_clc_wait_msg(new_smc, pclc, SMC_CLC_MAX_LEN,
+                                      SMC_CLC_PROPOSAL);
+       if (reason_code) {
+               smc_listen_decline(new_smc, reason_code, 0);
+               return;
        }
-       goto out_connected;
 
-out_err_unlock:
-       if (local_contact == SMC_FIRST_CONTACT)
-               smc_lgr_forget(new_smc->conn.lgr);
+       /* IPSec connections opt out of SMC-R optimizations */
+       if (using_ipsec(new_smc)) {
+               smc_listen_decline(new_smc, SMC_CLC_DECL_IPSEC, 0);
+               return;
+       }
+
+       mutex_lock(&smc_create_lgr_pending);
+       smc_close_init(new_smc);
+       smc_rx_init(new_smc);
+       smc_tx_init(new_smc);
+
+       /* check if RDMA is available */
+       if (smc_check_rdma(new_smc, &ibdev, &ibport) ||
+           smc_listen_rdma_check(new_smc, pclc) ||
+           smc_listen_rdma_init(new_smc, pclc, ibdev, ibport,
+                                &local_contact) ||
+           smc_listen_rdma_reg(new_smc, local_contact)) {
+               /* SMC not supported, decline */
+               mutex_unlock(&smc_create_lgr_pending);
+               smc_listen_decline(new_smc, SMC_CLC_DECL_CNFERR, local_contact);
+               return;
+       }
+
+       /* send SMC Accept CLC message */
+       rc = smc_clc_send_accept(new_smc, local_contact);
+       if (rc) {
+               mutex_unlock(&smc_create_lgr_pending);
+               smc_listen_decline(new_smc, rc, local_contact);
+               return;
+       }
+
+       /* receive SMC Confirm CLC message */
+       reason_code = smc_clc_wait_msg(new_smc, &cclc, sizeof(cclc),
+                                      SMC_CLC_CONFIRM);
+       if (reason_code) {
+               mutex_unlock(&smc_create_lgr_pending);
+               smc_listen_decline(new_smc, reason_code, local_contact);
+               return;
+       }
+
+       /* finish worker */
+       smc_listen_rdma_finish(new_smc, &cclc, local_contact);
+       smc_conn_save_peer_info(new_smc, &cclc);
        mutex_unlock(&smc_create_lgr_pending);
-out_err:
-       if (newsmcsk->sk_state == SMC_INIT)
-               sock_put(&new_smc->sk); /* passive closing */
-       newsmcsk->sk_state = SMC_CLOSED;
-       smc_conn_free(&new_smc->conn);
-       goto enqueue; /* queue new sock with sk_err set */
+       smc_listen_out_connected(new_smc);
 }
 
 static void smc_tcp_listen_work(struct work_struct *work)
@@ -1215,7 +1300,7 @@ static __poll_t smc_poll(struct file *file, struct socket *sock,
                        if (sk->sk_state == SMC_INIT &&
                            mask & EPOLLOUT &&
                            smc->clcsock->sk->sk_state != TCP_CLOSE) {
-                               rc = smc_connect_rdma(smc);
+                               rc = __smc_connect(smc);
                                if (rc < 0)
                                        mask |= EPOLLERR;
                                /* success cases including fallback */
@@ -1251,6 +1336,8 @@ static __poll_t smc_poll(struct file *file, struct socket *sock,
                        if (sk->sk_state == SMC_APPCLOSEWAIT1)
                                mask |= EPOLLIN;
                }
+               if (smc->conn.urg_state == SMC_URG_VALID)
+                       mask |= EPOLLPRI;
 
        }
        release_sock(sk);
@@ -1353,14 +1440,14 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
                break;
        case TCP_NODELAY:
                if (sk->sk_state != SMC_INIT && sk->sk_state != SMC_LISTEN) {
-                       if (val)
+                       if (val && !smc->use_fallback)
                                mod_delayed_work(system_wq, &smc->conn.tx_work,
                                                 0);
                }
                break;
        case TCP_CORK:
                if (sk->sk_state != SMC_INIT && sk->sk_state != SMC_LISTEN) {
-                       if (!val)
+                       if (!val && !smc->use_fallback)
                                mod_delayed_work(system_wq, &smc->conn.tx_work,
                                                 0);
                }
@@ -1390,10 +1477,13 @@ static int smc_getsockopt(struct socket *sock, int level, int optname,
 static int smc_ioctl(struct socket *sock, unsigned int cmd,
                     unsigned long arg)
 {
+       union smc_host_cursor cons, urg;
+       struct smc_connection *conn;
        struct smc_sock *smc;
        int answ;
 
        smc = smc_sk(sock->sk);
+       conn = &smc->conn;
        if (smc->use_fallback) {
                if (!smc->clcsock)
                        return -EBADF;
@@ -1403,20 +1493,49 @@ static int smc_ioctl(struct socket *sock, unsigned int cmd,
        case SIOCINQ: /* same as FIONREAD */
                if (smc->sk.sk_state == SMC_LISTEN)
                        return -EINVAL;
-               answ = atomic_read(&smc->conn.bytes_to_rcv);
+               if (smc->sk.sk_state == SMC_INIT ||
+                   smc->sk.sk_state == SMC_CLOSED)
+                       answ = 0;
+               else
+                       answ = atomic_read(&smc->conn.bytes_to_rcv);
                break;
        case SIOCOUTQ:
                /* output queue size (not send + not acked) */
                if (smc->sk.sk_state == SMC_LISTEN)
                        return -EINVAL;
-               answ = smc->conn.sndbuf_size -
+               if (smc->sk.sk_state == SMC_INIT ||
+                   smc->sk.sk_state == SMC_CLOSED)
+                       answ = 0;
+               else
+                       answ = smc->conn.sndbuf_desc->len -
                                        atomic_read(&smc->conn.sndbuf_space);
                break;
        case SIOCOUTQNSD:
                /* output queue size (not send only) */
                if (smc->sk.sk_state == SMC_LISTEN)
                        return -EINVAL;
-               answ = smc_tx_prepared_sends(&smc->conn);
+               if (smc->sk.sk_state == SMC_INIT ||
+                   smc->sk.sk_state == SMC_CLOSED)
+                       answ = 0;
+               else
+                       answ = smc_tx_prepared_sends(&smc->conn);
+               break;
+       case SIOCATMARK:
+               if (smc->sk.sk_state == SMC_LISTEN)
+                       return -EINVAL;
+               if (smc->sk.sk_state == SMC_INIT ||
+                   smc->sk.sk_state == SMC_CLOSED) {
+                       answ = 0;
+               } else {
+                       smc_curs_write(&cons,
+                              smc_curs_read(&conn->local_tx_ctrl.cons, conn),
+                                      conn);
+                       smc_curs_write(&urg,
+                                      smc_curs_read(&conn->urg_curs, conn),
+                                      conn);
+                       answ = smc_curs_diff(conn->rmb_desc->len,
+                                            &cons, &urg) == 1;
+               }
                break;
        default:
                return -ENOIOCTLCMD;
@@ -1625,18 +1744,7 @@ out_pnet:
 
 static void __exit smc_exit(void)
 {
-       struct smc_link_group *lgr, *lg;
-       LIST_HEAD(lgr_freeing_list);
-
-       spin_lock_bh(&smc_lgr_list.lock);
-       if (!list_empty(&smc_lgr_list.list))
-               list_splice_init(&smc_lgr_list.list, &lgr_freeing_list);
-       spin_unlock_bh(&smc_lgr_list.lock);
-       list_for_each_entry_safe(lgr, lg, &lgr_freeing_list, list) {
-               list_del_init(&lgr->list);
-               cancel_delayed_work_sync(&lgr->free_work);
-               smc_lgr_free(lgr); /* free link group */
-       }
+       smc_core_exit();
        static_branch_disable(&tcp_have_smc);
        smc_ib_unregister_client();
        sock_unregister(PF_SMC);
index ec209cd..51ae1f1 100644 (file)
@@ -114,11 +114,17 @@ struct smc_host_cdc_msg {         /* Connection Data Control message */
        u8                              reserved[18];
 } __aligned(8);
 
+enum smc_urg_state {
+       SMC_URG_VALID,                  /* data present */
+       SMC_URG_NOTYET,                 /* data pending */
+       SMC_URG_READ                    /* data was already read */
+};
+
 struct smc_connection {
        struct rb_node          alert_node;
        struct smc_link_group   *lgr;           /* link group of connection */
        u32                     alert_token_local; /* unique conn. id */
-       u8                      peer_conn_idx;  /* from tcp handshake */
+       u8                      peer_rmbe_idx;  /* from tcp handshake */
        int                     peer_rmbe_size; /* size of peer rx buffer */
        atomic_t                peer_rmbe_space;/* remaining free bytes in peer
                                                 * rmbe
@@ -126,9 +132,7 @@ struct smc_connection {
        int                     rtoken_idx;     /* idx to peer RMB rkey/addr */
 
        struct smc_buf_desc     *sndbuf_desc;   /* send buffer descriptor */
-       int                     sndbuf_size;    /* sndbuf size <== sock wmem */
        struct smc_buf_desc     *rmb_desc;      /* RMBE descriptor */
-       int                     rmbe_size;      /* RMBE size <== sock rmem */
        int                     rmbe_size_short;/* compressed notation */
        int                     rmbe_update_limit;
                                                /* lower limit for consumer
@@ -153,6 +157,7 @@ struct smc_connection {
        u16                     tx_cdc_seq;     /* sequence # for CDC send */
        spinlock_t              send_lock;      /* protect wr_sends */
        struct delayed_work     tx_work;        /* retry of smc_cdc_msg_send */
+       u32                     tx_off;         /* base offset in peer rmb */
 
        struct smc_host_cdc_msg local_rx_ctrl;  /* filled during event_handl.
                                                 * .prod cf. TCP rcv_nxt
@@ -161,6 +166,15 @@ struct smc_connection {
        union smc_host_cursor   rx_curs_confirmed; /* confirmed to peer
                                                    * source of snd_una ?
                                                    */
+       union smc_host_cursor   urg_curs;       /* points at urgent byte */
+       enum smc_urg_state      urg_state;
+       bool                    urg_tx_pend;    /* urgent data staged */
+       bool                    urg_rx_skip_pend;
+                                               /* indicate urgent oob data
+                                                * read, but previous regular
+                                                * data still pending
+                                                */
+       char                    urg_rx_byte;    /* urgent byte */
        atomic_t                bytes_to_rcv;   /* arrived data,
                                                 * not yet received
                                                 */
@@ -221,41 +235,6 @@ static inline u32 ntoh24(u8 *net)
        return be32_to_cpu(t);
 }
 
-#define SMC_BUF_MIN_SIZE 16384         /* minimum size of an RMB */
-
-#define SMC_RMBE_SIZES 16      /* number of distinct sizes for an RMBE */
-/* theoretically, the RFC states that largest size would be 512K,
- * i.e. compressed 5 and thus 6 sizes (0..5), despite
- * struct smc_clc_msg_accept_confirm.rmbe_size being a 4 bit value (0..15)
- */
-
-/* convert the RMB size into the compressed notation - minimum 16K.
- * In contrast to plain ilog2, this rounds towards the next power of 2,
- * so the socket application gets at least its desired sndbuf / rcvbuf size.
- */
-static inline u8 smc_compress_bufsize(int size)
-{
-       u8 compressed;
-
-       if (size <= SMC_BUF_MIN_SIZE)
-               return 0;
-
-       size = (size - 1) >> 14;
-       compressed = ilog2(size) + 1;
-       if (compressed >= SMC_RMBE_SIZES)
-               compressed = SMC_RMBE_SIZES - 1;
-       return compressed;
-}
-
-/* convert the RMB size from compressed notation into integer */
-static inline int smc_uncompress_bufsize(u8 compressed)
-{
-       u32 size;
-
-       size = 0x00000001 << (((int)compressed) + 14);
-       return (int)size;
-}
-
 #ifdef CONFIG_XFRM
 static inline bool using_ipsec(struct smc_sock *smc)
 {
@@ -269,12 +248,6 @@ static inline bool using_ipsec(struct smc_sock *smc)
 }
 #endif
 
-struct smc_clc_msg_local;
-
-void smc_conn_free(struct smc_connection *conn);
-int smc_conn_create(struct smc_sock *smc,
-                   struct smc_ib_device *smcibdev, u8 ibport,
-                   struct smc_clc_msg_local *lcl, int srv_first_contact);
 struct sock *smc_accept_dequeue(struct sock *parent, struct socket *new_sock);
 void smc_close_non_accepted(struct sock *sk);
 
index 42ad573..a7e8d63 100644 (file)
@@ -44,13 +44,13 @@ static void smc_cdc_tx_handler(struct smc_wr_tx_pend_priv *pnd_snd,
        smc = container_of(cdcpend->conn, struct smc_sock, conn);
        bh_lock_sock(&smc->sk);
        if (!wc_status) {
-               diff = smc_curs_diff(cdcpend->conn->sndbuf_size,
+               diff = smc_curs_diff(cdcpend->conn->sndbuf_desc->len,
                                     &cdcpend->conn->tx_curs_fin,
                                     &cdcpend->cursor);
                /* sndbuf_space is decreased in smc_sendmsg */
                smp_mb__before_atomic();
                atomic_add(diff, &cdcpend->conn->sndbuf_space);
-               /* guarantee 0 <= sndbuf_space <= sndbuf_size */
+               /* guarantee 0 <= sndbuf_space <= sndbuf_desc->len */
                smp_mb__after_atomic();
                smc_curs_write(&cdcpend->conn->tx_curs_fin,
                               smc_curs_read(&cdcpend->cursor, cdcpend->conn),
@@ -164,20 +164,35 @@ static inline bool smc_cdc_before(u16 seq1, u16 seq2)
        return (s16)(seq1 - seq2) < 0;
 }
 
+static void smc_cdc_handle_urg_data_arrival(struct smc_sock *smc,
+                                           int *diff_prod)
+{
+       struct smc_connection *conn = &smc->conn;
+       char *base;
+
+       /* new data included urgent business */
+       smc_curs_write(&conn->urg_curs,
+                      smc_curs_read(&conn->local_rx_ctrl.prod, conn),
+                      conn);
+       conn->urg_state = SMC_URG_VALID;
+       if (!sock_flag(&smc->sk, SOCK_URGINLINE))
+               /* we'll skip the urgent byte, so don't account for it */
+               (*diff_prod)--;
+       base = (char *)conn->rmb_desc->cpu_addr;
+       if (conn->urg_curs.count)
+               conn->urg_rx_byte = *(base + conn->urg_curs.count - 1);
+       else
+               conn->urg_rx_byte = *(base + conn->rmb_desc->len - 1);
+       sk_send_sigurg(&smc->sk);
+}
+
 static void smc_cdc_msg_recv_action(struct smc_sock *smc,
-                                   struct smc_link *link,
                                    struct smc_cdc_msg *cdc)
 {
        union smc_host_cursor cons_old, prod_old;
        struct smc_connection *conn = &smc->conn;
        int diff_cons, diff_prod;
 
-       if (!cdc->prod_flags.failover_validation) {
-               if (smc_cdc_before(ntohs(cdc->seqno),
-                                  conn->local_rx_ctrl.seqno))
-                       /* received seqno is old */
-                       return;
-       }
        smc_curs_write(&prod_old,
                       smc_curs_read(&conn->local_rx_ctrl.prod, conn),
                       conn);
@@ -198,18 +213,28 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc,
                smp_mb__after_atomic();
        }
 
-       diff_prod = smc_curs_diff(conn->rmbe_size, &prod_old,
+       diff_prod = smc_curs_diff(conn->rmb_desc->len, &prod_old,
                                  &conn->local_rx_ctrl.prod);
        if (diff_prod) {
+               if (conn->local_rx_ctrl.prod_flags.urg_data_present)
+                       smc_cdc_handle_urg_data_arrival(smc, &diff_prod);
                /* bytes_to_rcv is decreased in smc_recvmsg */
                smp_mb__before_atomic();
                atomic_add(diff_prod, &conn->bytes_to_rcv);
-               /* guarantee 0 <= bytes_to_rcv <= rmbe_size */
+               /* guarantee 0 <= bytes_to_rcv <= rmb_desc->len */
                smp_mb__after_atomic();
                smc->sk.sk_data_ready(&smc->sk);
-       } else if ((conn->local_rx_ctrl.prod_flags.write_blocked) ||
-                  (conn->local_rx_ctrl.prod_flags.cons_curs_upd_req)) {
-               smc->sk.sk_data_ready(&smc->sk);
+       } else {
+               if (conn->local_rx_ctrl.prod_flags.write_blocked ||
+                   conn->local_rx_ctrl.prod_flags.cons_curs_upd_req ||
+                   conn->local_rx_ctrl.prod_flags.urg_data_pending) {
+                       if (conn->local_rx_ctrl.prod_flags.urg_data_pending)
+                               conn->urg_state = SMC_URG_NOTYET;
+                       /* force immediate tx of current consumer cursor, but
+                        * under send_lock to guarantee arrival in seqno-order
+                        */
+                       smc_tx_sndbuf_nonempty(conn);
+               }
        }
 
        /* piggy backed tx info */
@@ -219,6 +244,12 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc,
                /* trigger socket release if connection closed */
                smc_close_wake_tx_prepared(smc);
        }
+       if (diff_cons && conn->urg_tx_pend &&
+           atomic_read(&conn->peer_rmbe_space) == conn->peer_rmbe_size) {
+               /* urg data confirmed by peer, indicate we're ready for more */
+               conn->urg_tx_pend = false;
+               smc->sk.sk_write_space(&smc->sk);
+       }
 
        if (conn->local_rx_ctrl.conn_state_flags.peer_conn_abort) {
                smc->sk.sk_err = ECONNRESET;
@@ -236,26 +267,11 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc,
 }
 
 /* called under tasklet context */
-static inline void smc_cdc_msg_recv(struct smc_cdc_msg *cdc,
-                                   struct smc_link *link, u64 wr_id)
+static void smc_cdc_msg_recv(struct smc_sock *smc, struct smc_cdc_msg *cdc)
 {
-       struct smc_link_group *lgr = container_of(link, struct smc_link_group,
-                                                 lnk[SMC_SINGLE_LINK]);
-       struct smc_connection *connection;
-       struct smc_sock *smc;
-
-       /* lookup connection */
-       read_lock_bh(&lgr->conns_lock);
-       connection = smc_lgr_find_conn(ntohl(cdc->token), lgr);
-       if (!connection) {
-               read_unlock_bh(&lgr->conns_lock);
-               return;
-       }
-       smc = container_of(connection, struct smc_sock, conn);
        sock_hold(&smc->sk);
-       read_unlock_bh(&lgr->conns_lock);
        bh_lock_sock(&smc->sk);
-       smc_cdc_msg_recv_action(smc, link, cdc);
+       smc_cdc_msg_recv_action(smc, cdc);
        bh_unlock_sock(&smc->sk);
        sock_put(&smc->sk); /* no free sk in softirq-context */
 }
@@ -266,12 +282,31 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
 {
        struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
        struct smc_cdc_msg *cdc = buf;
+       struct smc_connection *conn;
+       struct smc_link_group *lgr;
+       struct smc_sock *smc;
 
        if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved))
                return; /* short message */
        if (cdc->len != SMC_WR_TX_SIZE)
                return; /* invalid message */
-       smc_cdc_msg_recv(cdc, link, wc->wr_id);
+
+       /* lookup connection */
+       lgr = container_of(link, struct smc_link_group, lnk[SMC_SINGLE_LINK]);
+       read_lock_bh(&lgr->conns_lock);
+       conn = smc_lgr_find_conn(ntohl(cdc->token), lgr);
+       read_unlock_bh(&lgr->conns_lock);
+       if (!conn)
+               return;
+       smc = container_of(conn, struct smc_sock, conn);
+
+       if (!cdc->prod_flags.failover_validation) {
+               if (smc_cdc_before(ntohs(cdc->seqno),
+                                  conn->local_rx_ctrl.seqno))
+                       /* received seqno is old */
+                       return;
+       }
+       smc_cdc_msg_recv(smc, cdc);
 }
 
 static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = {
index d2012fd..f60082f 100644 (file)
@@ -146,6 +146,19 @@ static inline int smc_curs_diff(unsigned int size,
        return max_t(int, 0, (new->count - old->count));
 }
 
+/* calculate cursor difference between old and new - returns negative
+ * value in case old > new
+ */
+static inline int smc_curs_comp(unsigned int size,
+                               union smc_host_cursor *old,
+                               union smc_host_cursor *new)
+{
+       if (old->wrap > new->wrap ||
+           (old->wrap == new->wrap && old->count > new->count))
+               return -smc_curs_diff(size, new, old);
+       return smc_curs_diff(size, old, new);
+}
+
 static inline void smc_host_cursor_to_cdc(union smc_cdc_cursor *peer,
                                          union smc_host_cursor *local,
                                          struct smc_connection *conn)
index 3a988c2..717449b 100644 (file)
@@ -316,7 +316,7 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
        if (clcm->type == SMC_CLC_DECLINE) {
                reason_code = SMC_CLC_DECL_REPLY;
                if (((struct smc_clc_msg_decline *)buf)->hdr.flag) {
-                       smc->conn.lgr->sync_err = true;
+                       smc->conn.lgr->sync_err = 1;
                        smc_lgr_terminate(smc->conn.lgr);
                }
        }
@@ -442,7 +442,7 @@ int smc_clc_send_confirm(struct smc_sock *smc)
        hton24(cclc.qpn, link->roce_qp->qp_num);
        cclc.rmb_rkey =
                htonl(conn->rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey);
-       cclc.conn_idx = 1; /* for now: 1 RMB = 1 RMBE */
+       cclc.rmbe_idx = 1; /* for now: 1 RMB = 1 RMBE */
        cclc.rmbe_alert_token = htonl(conn->alert_token_local);
        cclc.qp_mtu = min(link->path_mtu, link->peer_mtu);
        cclc.rmbe_size = conn->rmbe_size_short;
@@ -494,7 +494,7 @@ int smc_clc_send_accept(struct smc_sock *new_smc, int srv_first_contact)
        hton24(aclc.qpn, link->roce_qp->qp_num);
        aclc.rmb_rkey =
                htonl(conn->rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey);
-       aclc.conn_idx = 1;                      /* as long as 1 RMB = 1 RMBE */
+       aclc.rmbe_idx = 1;                      /* as long as 1 RMB = 1 RMBE */
        aclc.rmbe_alert_token = htonl(conn->alert_token_local);
        aclc.qp_mtu = link->path_mtu;
        aclc.rmbe_size = conn->rmbe_size_short,
index 63bf1dc..41ff9ea 100644 (file)
@@ -97,7 +97,7 @@ struct smc_clc_msg_accept_confirm {   /* clc accept / confirm message */
        struct smc_clc_msg_local lcl;
        u8 qpn[3];              /* QP number */
        __be32 rmb_rkey;        /* RMB rkey */
-       u8 conn_idx;            /* Connection index, which RMBE in RMB */
+       u8 rmbe_idx;            /* Index of RMBE in RMB */
        __be32 rmbe_alert_token;/* unique connection id */
 #if defined(__BIG_ENDIAN_BITFIELD)
        u8 rmbe_size : 4,       /* RMBE buf size (compressed notation) */
index 9c74844..add82b0 100644 (file)
 
 #define SMC_LGR_NUM_INCR               256
 #define SMC_LGR_FREE_DELAY_SERV                (600 * HZ)
-#define SMC_LGR_FREE_DELAY_CLNT                (SMC_LGR_FREE_DELAY_SERV + 10)
+#define SMC_LGR_FREE_DELAY_CLNT                (SMC_LGR_FREE_DELAY_SERV + 10 * HZ)
 
-static u32 smc_lgr_num;                        /* unique link group number */
+static struct smc_lgr_list smc_lgr_list = {    /* established link groups */
+       .lock = __SPIN_LOCK_UNLOCKED(smc_lgr_list.lock),
+       .list = LIST_HEAD_INIT(smc_lgr_list.list),
+       .num = 0,
+};
 
-static void smc_buf_free(struct smc_buf_desc *buf_desc, struct smc_link *lnk,
-                        bool is_rmb);
+static void smc_buf_free(struct smc_link_group *lgr, bool is_rmb,
+                        struct smc_buf_desc *buf_desc);
 
 static void smc_lgr_schedule_free_work(struct smc_link_group *lgr)
 {
@@ -148,8 +152,11 @@ static void smc_lgr_free_work(struct work_struct *work)
        list_del_init(&lgr->list); /* remove from smc_lgr_list */
 free:
        spin_unlock_bh(&smc_lgr_list.lock);
-       if (!delayed_work_pending(&lgr->free_work))
+       if (!delayed_work_pending(&lgr->free_work)) {
+               if (lgr->lnk[SMC_SINGLE_LINK].state != SMC_LNK_INACTIVE)
+                       smc_llc_link_inactive(&lgr->lnk[SMC_SINGLE_LINK]);
                smc_lgr_free(lgr);
+       }
 }
 
 /* create a new SMC link group */
@@ -169,7 +176,7 @@ static int smc_lgr_create(struct smc_sock *smc,
                goto out;
        }
        lgr->role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
-       lgr->sync_err = false;
+       lgr->sync_err = 0;
        memcpy(lgr->peer_systemid, peer_systemid, SMC_SYSTEMID_LEN);
        lgr->vlan_id = vlan_id;
        rwlock_init(&lgr->sndbufs_lock);
@@ -178,8 +185,8 @@ static int smc_lgr_create(struct smc_sock *smc,
                INIT_LIST_HEAD(&lgr->sndbufs[i]);
                INIT_LIST_HEAD(&lgr->rmbs[i]);
        }
-       smc_lgr_num += SMC_LGR_NUM_INCR;
-       memcpy(&lgr->id, (u8 *)&smc_lgr_num, SMC_LGR_ID_SIZE);
+       smc_lgr_list.num += SMC_LGR_NUM_INCR;
+       memcpy(&lgr->id, (u8 *)&smc_lgr_list.num, SMC_LGR_ID_SIZE);
        INIT_DELAYED_WORK(&lgr->free_work, smc_lgr_free_work);
        lgr->conns_all = RB_ROOT;
 
@@ -194,9 +201,12 @@ static int smc_lgr_create(struct smc_sock *smc,
                smc_ib_setup_per_ibdev(smcibdev);
        get_random_bytes(rndvec, sizeof(rndvec));
        lnk->psn_initial = rndvec[0] + (rndvec[1] << 8) + (rndvec[2] << 16);
-       rc = smc_wr_alloc_link_mem(lnk);
+       rc = smc_llc_link_init(lnk);
        if (rc)
                goto free_lgr;
+       rc = smc_wr_alloc_link_mem(lnk);
+       if (rc)
+               goto clear_llc_lnk;
        rc = smc_ib_create_protection_domain(lnk);
        if (rc)
                goto free_link_mem;
@@ -206,10 +216,6 @@ static int smc_lgr_create(struct smc_sock *smc,
        rc = smc_wr_create_link(lnk);
        if (rc)
                goto destroy_qp;
-       init_completion(&lnk->llc_confirm);
-       init_completion(&lnk->llc_confirm_resp);
-       init_completion(&lnk->llc_add);
-       init_completion(&lnk->llc_add_resp);
 
        smc->conn.lgr = lgr;
        rwlock_init(&lgr->conns_lock);
@@ -224,6 +230,8 @@ dealloc_pd:
        smc_ib_dealloc_protection_domain(lnk);
 free_link_mem:
        smc_wr_free_link_mem(lnk);
+clear_llc_lnk:
+       smc_llc_link_clear(lnk);
 free_lgr:
        kfree(lgr);
 out:
@@ -232,26 +240,21 @@ out:
 
 static void smc_buf_unuse(struct smc_connection *conn)
 {
-       if (conn->sndbuf_desc) {
+       if (conn->sndbuf_desc)
                conn->sndbuf_desc->used = 0;
-               conn->sndbuf_size = 0;
-       }
        if (conn->rmb_desc) {
                if (!conn->rmb_desc->regerr) {
                        conn->rmb_desc->reused = 1;
                        conn->rmb_desc->used = 0;
-                       conn->rmbe_size = 0;
                } else {
                        /* buf registration failed, reuse not possible */
                        struct smc_link_group *lgr = conn->lgr;
-                       struct smc_link *lnk;
 
                        write_lock_bh(&lgr->rmbs_lock);
                        list_del(&conn->rmb_desc->list);
                        write_unlock_bh(&lgr->rmbs_lock);
 
-                       lnk = &lgr->lnk[SMC_SINGLE_LINK];
-                       smc_buf_free(conn->rmb_desc, lnk, true);
+                       smc_buf_free(lgr, true, conn->rmb_desc);
                }
        }
 }
@@ -269,6 +272,7 @@ void smc_conn_free(struct smc_connection *conn)
 static void smc_link_clear(struct smc_link *lnk)
 {
        lnk->peer_qpn = 0;
+       smc_llc_link_clear(lnk);
        smc_ib_modify_qp_reset(lnk);
        smc_wr_free_link(lnk);
        smc_ib_destroy_queue_pair(lnk);
@@ -276,9 +280,11 @@ static void smc_link_clear(struct smc_link *lnk)
        smc_wr_free_link_mem(lnk);
 }
 
-static void smc_buf_free(struct smc_buf_desc *buf_desc, struct smc_link *lnk,
-                        bool is_rmb)
+static void smc_buf_free(struct smc_link_group *lgr, bool is_rmb,
+                        struct smc_buf_desc *buf_desc)
 {
+       struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];
+
        if (is_rmb) {
                if (buf_desc->mr_rx[SMC_SINGLE_LINK])
                        smc_ib_put_memory_region(
@@ -297,7 +303,6 @@ static void smc_buf_free(struct smc_buf_desc *buf_desc, struct smc_link *lnk,
 
 static void __smc_lgr_free_bufs(struct smc_link_group *lgr, bool is_rmb)
 {
-       struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];
        struct smc_buf_desc *buf_desc, *bf_desc;
        struct list_head *buf_list;
        int i;
@@ -310,7 +315,7 @@ static void __smc_lgr_free_bufs(struct smc_link_group *lgr, bool is_rmb)
                list_for_each_entry_safe(buf_desc, bf_desc, buf_list,
                                         list) {
                        list_del(&buf_desc->list);
-                       smc_buf_free(buf_desc, lnk, is_rmb);
+                       smc_buf_free(lgr, is_rmb, buf_desc);
                }
        }
 }
@@ -326,7 +331,6 @@ static void smc_lgr_free_bufs(struct smc_link_group *lgr)
 /* remove a link group */
 void smc_lgr_free(struct smc_link_group *lgr)
 {
-       smc_llc_link_flush(&lgr->lnk[SMC_SINGLE_LINK]);
        smc_lgr_free_bufs(lgr);
        smc_link_clear(&lgr->lnk[SMC_SINGLE_LINK]);
        kfree(lgr);
@@ -342,13 +346,17 @@ void smc_lgr_forget(struct smc_link_group *lgr)
 }
 
 /* terminate linkgroup abnormally */
-void smc_lgr_terminate(struct smc_link_group *lgr)
+static void __smc_lgr_terminate(struct smc_link_group *lgr)
 {
        struct smc_connection *conn;
        struct smc_sock *smc;
        struct rb_node *node;
 
-       smc_lgr_forget(lgr);
+       if (lgr->terminating)
+               return; /* lgr already terminating */
+       lgr->terminating = 1;
+       if (!list_empty(&lgr->list)) /* forget lgr */
+               list_del_init(&lgr->list);
        smc_llc_link_inactive(&lgr->lnk[SMC_SINGLE_LINK]);
 
        write_lock_bh(&lgr->conns_lock);
@@ -370,6 +378,27 @@ void smc_lgr_terminate(struct smc_link_group *lgr)
        smc_lgr_schedule_free_work(lgr);
 }
 
+void smc_lgr_terminate(struct smc_link_group *lgr)
+{
+       spin_lock_bh(&smc_lgr_list.lock);
+       __smc_lgr_terminate(lgr);
+       spin_unlock_bh(&smc_lgr_list.lock);
+}
+
+/* Called when IB port is terminated */
+void smc_port_terminate(struct smc_ib_device *smcibdev, u8 ibport)
+{
+       struct smc_link_group *lgr, *l;
+
+       spin_lock_bh(&smc_lgr_list.lock);
+       list_for_each_entry_safe(lgr, l, &smc_lgr_list.list, list) {
+               if (lgr->lnk[SMC_SINGLE_LINK].smcibdev == smcibdev &&
+                   lgr->lnk[SMC_SINGLE_LINK].ibport == ibport)
+                       __smc_lgr_terminate(lgr);
+       }
+       spin_unlock_bh(&smc_lgr_list.lock);
+}
+
 /* Determine vlan of internal TCP socket.
  * @vlan_id: address to store the determined vlan id into
  */
@@ -454,10 +483,10 @@ int smc_conn_create(struct smc_sock *smc,
                    struct smc_clc_msg_local *lcl, int srv_first_contact)
 {
        struct smc_connection *conn = &smc->conn;
+       int local_contact = SMC_FIRST_CONTACT;
        struct smc_link_group *lgr;
        unsigned short vlan_id;
        enum smc_lgr_role role;
-       int local_contact = SMC_FIRST_CONTACT;
        int rc = 0;
 
        role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
@@ -515,6 +544,7 @@ create:
        }
        conn->local_tx_ctrl.common.type = SMC_CDC_MSG_TYPE;
        conn->local_tx_ctrl.len = SMC_WR_TX_SIZE;
+       conn->urg_state = SMC_URG_READ;
 #ifndef KERNEL_HAS_ATOMIC64
        spin_lock_init(&conn->acurs_lock);
 #endif
@@ -523,14 +553,39 @@ out:
        return rc ? rc : local_contact;
 }
 
+/* convert the RMB size into the compressed notation - minimum 16K.
+ * In contrast to plain ilog2, this rounds towards the next power of 2,
+ * so the socket application gets at least its desired sndbuf / rcvbuf size.
+ */
+static u8 smc_compress_bufsize(int size)
+{
+       u8 compressed;
+
+       if (size <= SMC_BUF_MIN_SIZE)
+               return 0;
+
+       size = (size - 1) >> 14;
+       compressed = ilog2(size) + 1;
+       if (compressed >= SMC_RMBE_SIZES)
+               compressed = SMC_RMBE_SIZES - 1;
+       return compressed;
+}
+
+/* convert the RMB size from compressed notation into integer */
+int smc_uncompress_bufsize(u8 compressed)
+{
+       u32 size;
+
+       size = 0x00000001 << (((int)compressed) + 14);
+       return (int)size;
+}
+
 /* try to reuse a sndbuf or rmb description slot for a certain
  * buffer size; if not available, return NULL
  */
-static inline
-struct smc_buf_desc *smc_buf_get_slot(struct smc_link_group *lgr,
-                                     int compressed_bufsize,
-                                     rwlock_t *lock,
-                                     struct list_head *buf_list)
+static struct smc_buf_desc *smc_buf_get_slot(int compressed_bufsize,
+                                            rwlock_t *lock,
+                                            struct list_head *buf_list)
 {
        struct smc_buf_desc *buf_slot;
 
@@ -582,7 +637,7 @@ static struct smc_buf_desc *smc_new_buf_create(struct smc_link_group *lgr,
        rc = sg_alloc_table(&buf_desc->sgt[SMC_SINGLE_LINK], 1,
                            GFP_KERNEL);
        if (rc) {
-               smc_buf_free(buf_desc, lnk, is_rmb);
+               smc_buf_free(lgr, is_rmb, buf_desc);
                return ERR_PTR(rc);
        }
        sg_set_buf(buf_desc->sgt[SMC_SINGLE_LINK].sgl,
@@ -593,7 +648,7 @@ static struct smc_buf_desc *smc_new_buf_create(struct smc_link_group *lgr,
                               is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
        /* SMC protocol depends on mapping to one DMA address only */
        if (rc != 1)  {
-               smc_buf_free(buf_desc, lnk, is_rmb);
+               smc_buf_free(lgr, is_rmb, buf_desc);
                return ERR_PTR(-EAGAIN);
        }
 
@@ -604,19 +659,20 @@ static struct smc_buf_desc *smc_new_buf_create(struct smc_link_group *lgr,
                                              IB_ACCESS_LOCAL_WRITE,
                                              buf_desc);
                if (rc) {
-                       smc_buf_free(buf_desc, lnk, is_rmb);
+                       smc_buf_free(lgr, is_rmb, buf_desc);
                        return ERR_PTR(rc);
                }
        }
 
+       buf_desc->len = bufsize;
        return buf_desc;
 }
 
 static int __smc_buf_create(struct smc_sock *smc, bool is_rmb)
 {
+       struct smc_buf_desc *buf_desc = ERR_PTR(-ENOMEM);
        struct smc_connection *conn = &smc->conn;
        struct smc_link_group *lgr = conn->lgr;
-       struct smc_buf_desc *buf_desc = ERR_PTR(-ENOMEM);
        struct list_head *buf_list;
        int bufsize, bufsize_short;
        int sk_buf_size;
@@ -644,7 +700,7 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_rmb)
                        continue;
 
                /* check for reusable slot in the link group */
-               buf_desc = smc_buf_get_slot(lgr, bufsize_short, lock, buf_list);
+               buf_desc = smc_buf_get_slot(bufsize_short, lock, buf_list);
                if (buf_desc) {
                        memset(buf_desc->cpu_addr, 0, bufsize);
                        break; /* found reusable slot */
@@ -668,14 +724,12 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_rmb)
 
        if (is_rmb) {
                conn->rmb_desc = buf_desc;
-               conn->rmbe_size = bufsize;
                conn->rmbe_size_short = bufsize_short;
                smc->sk.sk_rcvbuf = bufsize * 2;
                atomic_set(&conn->bytes_to_rcv, 0);
                conn->rmbe_update_limit = smc_rmb_wnd_update_limit(bufsize);
        } else {
                conn->sndbuf_desc = buf_desc;
-               conn->sndbuf_size = bufsize;
                smc->sk.sk_sndbuf = bufsize * 2;
                atomic_set(&conn->sndbuf_space, bufsize);
        }
@@ -731,8 +785,7 @@ int smc_buf_create(struct smc_sock *smc)
        /* create rmb */
        rc = __smc_buf_create(smc, true);
        if (rc)
-               smc_buf_free(smc->conn.sndbuf_desc,
-                            &smc->conn.lgr->lnk[SMC_SINGLE_LINK], false);
+               smc_buf_free(smc->conn.lgr, false, smc->conn.sndbuf_desc);
        return rc;
 }
 
@@ -799,3 +852,21 @@ int smc_rmb_rtoken_handling(struct smc_connection *conn,
                return conn->rtoken_idx;
        return 0;
 }
+
+/* Called (from smc_exit) when module is removed */
+void smc_core_exit(void)
+{
+       struct smc_link_group *lgr, *lg;
+       LIST_HEAD(lgr_freeing_list);
+
+       spin_lock_bh(&smc_lgr_list.lock);
+       if (!list_empty(&smc_lgr_list.list))
+               list_splice_init(&smc_lgr_list.list, &lgr_freeing_list);
+       spin_unlock_bh(&smc_lgr_list.lock);
+       list_for_each_entry_safe(lgr, lg, &lgr_freeing_list, list) {
+               list_del_init(&lgr->list);
+               smc_llc_link_inactive(&lgr->lnk[SMC_SINGLE_LINK]);
+               cancel_delayed_work_sync(&lgr->free_work);
+               smc_lgr_free(lgr); /* free link group */
+       }
+}
index fca8624..93cb352 100644 (file)
 struct smc_lgr_list {                  /* list of link group definition */
        struct list_head        list;
        spinlock_t              lock;   /* protects list of link groups */
+       u32                     num;    /* unique link group number */
 };
 
-extern struct smc_lgr_list     smc_lgr_list; /* list of link groups */
-
 enum smc_lgr_role {            /* possible roles of a link group */
        SMC_CLNT,       /* client */
        SMC_SERV        /* server */
@@ -96,6 +95,7 @@ struct smc_link {
        u8                      link_id;        /* unique # within link group */
 
        enum smc_link_state     state;          /* state of link */
+       struct workqueue_struct *llc_wq;        /* single thread work queue */
        struct completion       llc_confirm;    /* wait for rx of conf link */
        struct completion       llc_confirm_resp; /* wait 4 rx of cnf lnk rsp */
        int                     llc_confirm_rc; /* rc from confirm link msg */
@@ -105,6 +105,8 @@ struct smc_link {
        struct delayed_work     llc_testlink_wrk; /* testlink worker */
        struct completion       llc_testlink_resp; /* wait for rx of testlink */
        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 */
 };
 
 /* For now we just allow one parallel link per link group. The SMC protocol
@@ -121,6 +123,7 @@ struct smc_buf_desc {
        struct list_head        list;
        void                    *cpu_addr;      /* virtual address of buffer */
        struct page             *pages;
+       int                     len;            /* length of buffer */
        struct sg_table         sgt[SMC_LINKS_PER_LGR_MAX];/* virtual buffer */
        struct ib_mr            *mr_rx[SMC_LINKS_PER_LGR_MAX];
                                                /* for rmb only: memory region
@@ -138,6 +141,12 @@ struct smc_rtoken {                                /* address/key of remote RMB */
 };
 
 #define SMC_LGR_ID_SIZE                4
+#define SMC_BUF_MIN_SIZE       16384   /* minimum size of an RMB */
+#define SMC_RMBE_SIZES         16      /* number of distinct RMBE sizes */
+/* theoretically, the RFC states that largest size would be 512K,
+ * i.e. compressed 5 and thus 6 sizes (0..5), despite
+ * struct smc_clc_msg_accept_confirm.rmbe_size being a 4 bit value (0..15)
+ */
 
 struct smc_link_group {
        struct list_head        list;
@@ -163,7 +172,8 @@ struct smc_link_group {
 
        u8                      id[SMC_LGR_ID_SIZE];    /* unique lgr id */
        struct delayed_work     free_work;      /* delayed freeing of an lgr */
-       bool                    sync_err;       /* lgr no longer fits to peer */
+       u8                      sync_err : 1;   /* lgr no longer fits to peer */
+       u8                      terminating : 1;/* lgr is terminating */
 };
 
 /* Find the connection associated with the given alert token in the link group.
@@ -201,11 +211,14 @@ static inline struct smc_connection *smc_lgr_find_conn(
 
 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);
 int smc_buf_create(struct smc_sock *smc);
+int smc_uncompress_bufsize(u8 compressed);
 int smc_rmb_rtoken_handling(struct smc_connection *conn,
                            struct smc_clc_msg_accept_confirm *clc);
 int smc_rtoken_add(struct smc_link_group *lgr, __be64 nw_vaddr, __be32 nw_rkey);
@@ -214,4 +227,9 @@ void smc_sndbuf_sync_sg_for_cpu(struct smc_connection *conn);
 void smc_sndbuf_sync_sg_for_device(struct smc_connection *conn);
 void smc_rmb_sync_sg_for_cpu(struct smc_connection *conn);
 void smc_rmb_sync_sg_for_device(struct smc_connection *conn);
+void smc_conn_free(struct smc_connection *conn);
+int smc_conn_create(struct smc_sock *smc,
+                   struct smc_ib_device *smcibdev, u8 ibport,
+                   struct smc_clc_msg_local *lcl, int srv_first_contact);
+void smc_core_exit(void);
 #endif
index 05dd7e6..8393544 100644 (file)
@@ -101,8 +101,9 @@ static int __smc_diag_dump(struct sock *sk, struct sk_buff *skb,
                struct smc_connection *conn = &smc->conn;
                struct smc_diag_conninfo cinfo = {
                        .token = conn->alert_token_local,
-                       .sndbuf_size = conn->sndbuf_size,
-                       .rmbe_size = conn->rmbe_size,
+                       .sndbuf_size = conn->sndbuf_desc ?
+                               conn->sndbuf_desc->len : 0,
+                       .rmbe_size = conn->rmb_desc ? conn->rmb_desc->len : 0,
                        .peer_rmbe_size = conn->peer_rmbe_size,
 
                        .rx_prod.wrap = conn->local_rx_ctrl.prod.wrap,
index 26df554..0eed7ab 100644 (file)
@@ -143,17 +143,6 @@ out:
        return rc;
 }
 
-static void smc_ib_port_terminate(struct smc_ib_device *smcibdev, u8 ibport)
-{
-       struct smc_link_group *lgr, *l;
-
-       list_for_each_entry_safe(lgr, l, &smc_lgr_list.list, list) {
-               if (lgr->lnk[SMC_SINGLE_LINK].smcibdev == smcibdev &&
-                   lgr->lnk[SMC_SINGLE_LINK].ibport == ibport)
-                       smc_lgr_terminate(lgr);
-       }
-}
-
 /* process context wrapper for might_sleep smc_ib_remember_port_attr */
 static void smc_ib_port_event_work(struct work_struct *work)
 {
@@ -165,7 +154,7 @@ static void smc_ib_port_event_work(struct work_struct *work)
                smc_ib_remember_port_attr(smcibdev, port_idx + 1);
                clear_bit(port_idx, &smcibdev->port_event_mask);
                if (!smc_ib_port_active(smcibdev, port_idx + 1))
-                       smc_ib_port_terminate(smcibdev, port_idx + 1);
+                       smc_port_terminate(smcibdev, port_idx + 1);
        }
 }
 
index 33b4d85..5800a6b 100644 (file)
@@ -214,12 +214,11 @@ int smc_llc_send_confirm_link(struct smc_link *link, u8 mac[],
        return rc;
 }
 
-/* send ADD LINK request or response */
-int smc_llc_send_add_link(struct smc_link *link, u8 mac[],
-                         union ib_gid *gid,
-                         enum smc_llc_reqresp reqresp)
+/* send LLC confirm rkey request */
+static int smc_llc_send_confirm_rkey(struct smc_link *link,
+                                    struct smc_buf_desc *rmb_desc)
 {
-       struct smc_llc_msg_add_link *addllc;
+       struct smc_llc_msg_confirm_rkey *rkeyllc;
        struct smc_wr_tx_pend_priv *pend;
        struct smc_wr_buf *wr_buf;
        int rc;
@@ -227,7 +226,25 @@ int smc_llc_send_add_link(struct smc_link *link, u8 mac[],
        rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
        if (rc)
                return rc;
-       addllc = (struct smc_llc_msg_add_link *)wr_buf;
+       rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf;
+       memset(rkeyllc, 0, sizeof(*rkeyllc));
+       rkeyllc->hd.common.type = SMC_LLC_CONFIRM_RKEY;
+       rkeyllc->hd.length = sizeof(struct smc_llc_msg_confirm_rkey);
+       rkeyllc->rtoken[0].rmb_key =
+               htonl(rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey);
+       rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64(
+               (u64)sg_dma_address(rmb_desc->sgt[SMC_SINGLE_LINK].sgl));
+       /* 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[],
+                                 union ib_gid *gid,
+                                 enum smc_llc_reqresp reqresp)
+{
        memset(addllc, 0, sizeof(*addllc));
        addllc->hd.common.type = SMC_LLC_ADD_LINK;
        addllc->hd.length = sizeof(struct smc_llc_msg_add_link);
@@ -239,16 +256,14 @@ int smc_llc_send_add_link(struct smc_link *link, u8 mac[],
        }
        memcpy(addllc->sender_mac, mac, ETH_ALEN);
        memcpy(addllc->sender_gid, gid, SMC_GID_SIZE);
-       /* send llc message */
-       rc = smc_wr_tx_send(link, pend);
-       return rc;
 }
 
-/* send DELETE LINK request or response */
-int smc_llc_send_delete_link(struct smc_link *link,
-                            enum smc_llc_reqresp reqresp)
+/* send ADD LINK request or response */
+int smc_llc_send_add_link(struct smc_link *link, u8 mac[],
+                         union ib_gid *gid,
+                         enum smc_llc_reqresp reqresp)
 {
-       struct smc_llc_msg_del_link *delllc;
+       struct smc_llc_msg_add_link *addllc;
        struct smc_wr_tx_pend_priv *pend;
        struct smc_wr_buf *wr_buf;
        int rc;
@@ -256,7 +271,18 @@ int smc_llc_send_delete_link(struct smc_link *link,
        rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
        if (rc)
                return rc;
-       delllc = (struct smc_llc_msg_del_link *)wr_buf;
+       addllc = (struct smc_llc_msg_add_link *)wr_buf;
+       smc_llc_prep_add_link(addllc, link, mac, gid, reqresp);
+       /* send llc message */
+       rc = smc_wr_tx_send(link, pend);
+       return rc;
+}
+
+/* prepare a delete link message */
+static void smc_llc_prep_delete_link(struct smc_llc_msg_del_link *delllc,
+                                    struct smc_link *link,
+                                    enum smc_llc_reqresp reqresp)
+{
        memset(delllc, 0, sizeof(*delllc));
        delllc->hd.common.type = SMC_LLC_DELETE_LINK;
        delllc->hd.length = sizeof(struct smc_llc_msg_add_link);
@@ -266,14 +292,29 @@ int smc_llc_send_delete_link(struct smc_link *link,
        delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
        delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
        delllc->link_num = link->link_id;
+}
+
+/* send DELETE LINK request or response */
+int smc_llc_send_delete_link(struct smc_link *link,
+                            enum smc_llc_reqresp reqresp)
+{
+       struct smc_llc_msg_del_link *delllc;
+       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;
+       delllc = (struct smc_llc_msg_del_link *)wr_buf;
+       smc_llc_prep_delete_link(delllc, link, reqresp);
        /* send llc message */
        rc = smc_wr_tx_send(link, pend);
        return rc;
 }
 
-/* send LLC test link request or response */
-int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16],
-                          enum smc_llc_reqresp reqresp)
+/* send LLC test link request */
+static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16])
 {
        struct smc_llc_msg_test_link *testllc;
        struct smc_wr_tx_pend_priv *pend;
@@ -287,28 +328,52 @@ int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16],
        memset(testllc, 0, sizeof(*testllc));
        testllc->hd.common.type = SMC_LLC_TEST_LINK;
        testllc->hd.length = sizeof(struct smc_llc_msg_test_link);
-       if (reqresp == SMC_LLC_RESP)
-               testllc->hd.flags |= SMC_LLC_FLAG_RESP;
        memcpy(testllc->user_data, user_data, sizeof(testllc->user_data));
        /* send llc message */
        rc = smc_wr_tx_send(link, pend);
        return rc;
 }
 
-/* send a prepared message */
-static int smc_llc_send_message(struct smc_link *link, void *llcbuf, int llclen)
+struct smc_llc_send_work {
+       struct work_struct work;
+       struct smc_link *link;
+       int llclen;
+       union smc_llc_msg llcbuf;
+};
+
+/* worker that sends a prepared message */
+static void smc_llc_send_message_work(struct work_struct *work)
 {
+       struct smc_llc_send_work *llcwrk = container_of(work,
+                                               struct smc_llc_send_work, work);
        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 (llcwrk->link->state == SMC_LNK_INACTIVE)
+               goto out;
+       rc = smc_llc_add_pending_send(llcwrk->link, &wr_buf, &pend);
        if (rc)
-               return rc;
-       memcpy(wr_buf, llcbuf, llclen);
-       /* send llc message */
-       rc = smc_wr_tx_send(link, pend);
-       return rc;
+               goto out;
+       memcpy(wr_buf, &llcwrk->llcbuf, llcwrk->llclen);
+       smc_wr_tx_send(llcwrk->link, pend);
+out:
+       kfree(llcwrk);
+}
+
+/* copy llcbuf and schedule an llc send on link */
+static int smc_llc_send_message(struct smc_link *link, void *llcbuf, int llclen)
+{
+       struct smc_llc_send_work *wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC);
+
+       if (!wrk)
+               return -ENOMEM;
+       INIT_WORK(&wrk->work, smc_llc_send_message_work);
+       wrk->link = link;
+       wrk->llclen = llclen;
+       memcpy(&wrk->llcbuf, llcbuf, llclen);
+       queue_work(link->llc_wq, &wrk->work);
+       return 0;
 }
 
 /********************************* receive ***********************************/
@@ -359,17 +424,18 @@ static void smc_llc_rx_add_link(struct smc_link *link,
                }
 
                if (lgr->role == SMC_SERV) {
-                       smc_llc_send_add_link(link,
+                       smc_llc_prep_add_link(llc, link,
                                        link->smcibdev->mac[link->ibport - 1],
                                        &link->smcibdev->gid[link->ibport - 1],
                                        SMC_LLC_REQ);
 
                } else {
-                       smc_llc_send_add_link(link,
+                       smc_llc_prep_add_link(llc, link,
                                        link->smcibdev->mac[link->ibport - 1],
                                        &link->smcibdev->gid[link->ibport - 1],
                                        SMC_LLC_RESP);
                }
+               smc_llc_send_message(link, llc, sizeof(*llc));
        }
 }
 
@@ -385,9 +451,11 @@ static void smc_llc_rx_delete_link(struct smc_link *link,
        } else {
                if (lgr->role == SMC_SERV) {
                        smc_lgr_forget(lgr);
-                       smc_llc_send_delete_link(link, SMC_LLC_REQ);
+                       smc_llc_prep_delete_link(llc, link, SMC_LLC_REQ);
+                       smc_llc_send_message(link, llc, sizeof(*llc));
                } else {
-                       smc_llc_send_delete_link(link, SMC_LLC_RESP);
+                       smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP);
+                       smc_llc_send_message(link, llc, sizeof(*llc));
                        smc_lgr_terminate(lgr);
                }
        }
@@ -400,7 +468,8 @@ static void smc_llc_rx_test_link(struct smc_link *link,
                if (link->state == SMC_LNK_ACTIVE)
                        complete(&link->llc_testlink_resp);
        } else {
-               smc_llc_send_test_link(link, llc->user_data, SMC_LLC_RESP);
+               llc->hd.flags |= SMC_LLC_FLAG_RESP;
+               smc_llc_send_message(link, llc, sizeof(*llc));
        }
 }
 
@@ -413,7 +482,9 @@ static void smc_llc_rx_confirm_rkey(struct smc_link *link,
        lgr = container_of(link, struct smc_link_group, lnk[SMC_SINGLE_LINK]);
 
        if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
-               /* unused as long as we don't send this type of msg */
+               link->llc_confirm_rkey_rc = llc->hd.flags &
+                                           SMC_LLC_FLAG_RKEY_NEG;
+               complete(&link->llc_confirm_rkey);
        } else {
                rc = smc_rtoken_add(lgr,
                                    llc->rtoken[0].rmb_vaddr,
@@ -424,7 +495,7 @@ static void smc_llc_rx_confirm_rkey(struct smc_link *link,
                llc->hd.flags |= SMC_LLC_FLAG_RESP;
                if (rc < 0)
                        llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
-               smc_llc_send_message(link, (void *)llc, sizeof(*llc));
+               smc_llc_send_message(link, llc, sizeof(*llc));
        }
 }
 
@@ -436,7 +507,7 @@ static void smc_llc_rx_confirm_rkey_cont(struct smc_link *link,
        } else {
                /* ignore rtokens for other links, we have only one link */
                llc->hd.flags |= SMC_LLC_FLAG_RESP;
-               smc_llc_send_message(link, (void *)llc, sizeof(*llc));
+               smc_llc_send_message(link, llc, sizeof(*llc));
        }
 }
 
@@ -464,7 +535,7 @@ static void smc_llc_rx_delete_rkey(struct smc_link *link,
                }
 
                llc->hd.flags |= SMC_LLC_FLAG_RESP;
-               smc_llc_send_message(link, (void *)llc, sizeof(*llc));
+               smc_llc_send_message(link, llc, sizeof(*llc));
        }
 }
 
@@ -477,6 +548,8 @@ static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
                return; /* short message */
        if (llc->raw.hdr.length != sizeof(*llc))
                return; /* invalid message */
+       if (link->state == SMC_LNK_INACTIVE)
+               return; /* link not active, drop msg */
 
        switch (llc->raw.hdr.common.type) {
        case SMC_LLC_TEST_LINK:
@@ -503,7 +576,7 @@ static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
        }
 }
 
-/***************************** worker ****************************************/
+/***************************** worker, utils *********************************/
 
 static void smc_llc_testlink_work(struct work_struct *work)
 {
@@ -524,7 +597,7 @@ static void smc_llc_testlink_work(struct work_struct *work)
                goto out;
        }
        reinit_completion(&link->llc_testlink_resp);
-       smc_llc_send_test_link(link, user_data, SMC_LLC_REQ);
+       smc_llc_send_test_link(link, user_data);
        /* receive TEST LINK response over RoCE fabric */
        rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,
                                                       SMC_LLC_WAIT_TIME);
@@ -534,18 +607,36 @@ static void smc_llc_testlink_work(struct work_struct *work)
        }
        next_interval = link->llc_testlink_time;
 out:
-       schedule_delayed_work(&link->llc_testlink_wrk, next_interval);
+       queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk,
+                          next_interval);
 }
 
-void smc_llc_link_active(struct smc_link *link, int testlink_time)
+int smc_llc_link_init(struct smc_link *link)
 {
+       struct smc_link_group *lgr = container_of(link, struct smc_link_group,
+                                                 lnk[SMC_SINGLE_LINK]);
+       link->llc_wq = alloc_ordered_workqueue("llc_wq-%x:%x)", WQ_MEM_RECLAIM,
+                                              *((u32 *)lgr->id),
+                                              link->link_id);
+       if (!link->llc_wq)
+               return -ENOMEM;
+       init_completion(&link->llc_confirm);
+       init_completion(&link->llc_confirm_resp);
+       init_completion(&link->llc_add);
+       init_completion(&link->llc_add_resp);
+       init_completion(&link->llc_confirm_rkey);
        init_completion(&link->llc_testlink_resp);
        INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
+       return 0;
+}
+
+void smc_llc_link_active(struct smc_link *link, int testlink_time)
+{
        link->state = SMC_LNK_ACTIVE;
        if (testlink_time) {
                link->llc_testlink_time = testlink_time * HZ;
-               schedule_delayed_work(&link->llc_testlink_wrk,
-                                     link->llc_testlink_time);
+               queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk,
+                                  link->llc_testlink_time);
        }
 }
 
@@ -557,9 +648,26 @@ void smc_llc_link_inactive(struct smc_link *link)
 }
 
 /* called in worker context */
-void smc_llc_link_flush(struct smc_link *link)
+void smc_llc_link_clear(struct smc_link *link)
+{
+       flush_workqueue(link->llc_wq);
+       destroy_workqueue(link->llc_wq);
+}
+
+/* register a new rtoken at the remote peer */
+int smc_llc_do_confirm_rkey(struct smc_link *link,
+                           struct smc_buf_desc *rmb_desc)
 {
-       cancel_delayed_work_sync(&link->llc_testlink_wrk);
+       int rc;
+
+       reinit_completion(&link->llc_confirm_rkey);
+       smc_llc_send_confirm_rkey(link, rmb_desc);
+       /* receive CONFIRM RKEY response from server over RoCE fabric */
+       rc = wait_for_completion_interruptible_timeout(&link->llc_confirm_rkey,
+                                                      SMC_LLC_WAIT_TIME);
+       if (rc <= 0 || link->llc_confirm_rkey_rc)
+               return -EFAULT;
+       return 0;
 }
 
 /***************************** init, exit, misc ******************************/
index d6e4211..65c8645 100644 (file)
@@ -42,11 +42,12 @@ int smc_llc_send_add_link(struct smc_link *link, u8 mac[], union ib_gid *gid,
                          enum smc_llc_reqresp reqresp);
 int smc_llc_send_delete_link(struct smc_link *link,
                             enum smc_llc_reqresp reqresp);
-int smc_llc_send_test_link(struct smc_link *lnk, u8 user_data[16],
-                          enum smc_llc_reqresp reqresp);
+int smc_llc_link_init(struct smc_link *link);
 void smc_llc_link_active(struct smc_link *link, int testlink_time);
 void smc_llc_link_inactive(struct smc_link *link);
-void smc_llc_link_flush(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_init(void) __init;
 
 #endif /* SMC_LLC_H */
index 74568cd..d7b88b2 100644 (file)
@@ -245,40 +245,45 @@ out:
 static int smc_pnet_fill_entry(struct net *net, struct smc_pnetentry *pnetelem,
                               struct nlattr *tb[])
 {
-       char *string, *ibname = NULL;
-       int rc = 0;
+       char *string, *ibname;
+       int rc;
 
        memset(pnetelem, 0, sizeof(*pnetelem));
        INIT_LIST_HEAD(&pnetelem->list);
-       if (tb[SMC_PNETID_NAME]) {
-               string = (char *)nla_data(tb[SMC_PNETID_NAME]);
-               if (!smc_pnetid_valid(string, pnetelem->pnet_name)) {
-                       rc = -EINVAL;
-                       goto error;
-               }
-       }
-       if (tb[SMC_PNETID_ETHNAME]) {
-               string = (char *)nla_data(tb[SMC_PNETID_ETHNAME]);
-               pnetelem->ndev = dev_get_by_name(net, string);
-               if (!pnetelem->ndev)
-                       return -ENOENT;
-       }
-       if (tb[SMC_PNETID_IBNAME]) {
-               ibname = (char *)nla_data(tb[SMC_PNETID_IBNAME]);
-               ibname = strim(ibname);
-               pnetelem->smcibdev = smc_pnet_find_ib(ibname);
-               if (!pnetelem->smcibdev) {
-                       rc = -ENOENT;
-                       goto error;
-               }
-       }
-       if (tb[SMC_PNETID_IBPORT]) {
-               pnetelem->ib_port = nla_get_u8(tb[SMC_PNETID_IBPORT]);
-               if (pnetelem->ib_port > SMC_MAX_PORTS) {
-                       rc = -EINVAL;
-                       goto error;
-               }
-       }
+
+       rc = -EINVAL;
+       if (!tb[SMC_PNETID_NAME])
+               goto error;
+       string = (char *)nla_data(tb[SMC_PNETID_NAME]);
+       if (!smc_pnetid_valid(string, pnetelem->pnet_name))
+               goto error;
+
+       rc = -EINVAL;
+       if (!tb[SMC_PNETID_ETHNAME])
+               goto error;
+       rc = -ENOENT;
+       string = (char *)nla_data(tb[SMC_PNETID_ETHNAME]);
+       pnetelem->ndev = dev_get_by_name(net, string);
+       if (!pnetelem->ndev)
+               goto error;
+
+       rc = -EINVAL;
+       if (!tb[SMC_PNETID_IBNAME])
+               goto error;
+       rc = -ENOENT;
+       ibname = (char *)nla_data(tb[SMC_PNETID_IBNAME]);
+       ibname = strim(ibname);
+       pnetelem->smcibdev = smc_pnet_find_ib(ibname);
+       if (!pnetelem->smcibdev)
+               goto error;
+
+       rc = -EINVAL;
+       if (!tb[SMC_PNETID_IBPORT])
+               goto error;
+       pnetelem->ib_port = nla_get_u8(tb[SMC_PNETID_IBPORT]);
+       if (pnetelem->ib_port < 1 || pnetelem->ib_port > SMC_MAX_PORTS)
+               goto error;
+
        return 0;
 
 error:
@@ -307,6 +312,8 @@ static int smc_pnet_get(struct sk_buff *skb, struct genl_info *info)
        void *hdr;
        int rc;
 
+       if (!info->attrs[SMC_PNETID_NAME])
+               return -EINVAL;
        pnetelem = smc_pnet_find_pnetid(
                                (char *)nla_data(info->attrs[SMC_PNETID_NAME]));
        if (!pnetelem)
@@ -359,6 +366,8 @@ static int smc_pnet_add(struct sk_buff *skb, struct genl_info *info)
 
 static int smc_pnet_del(struct sk_buff *skb, struct genl_info *info)
 {
+       if (!info->attrs[SMC_PNETID_NAME])
+               return -EINVAL;
        return smc_pnet_remove_by_pnetid(
                                (char *)nla_data(info->attrs[SMC_PNETID_NAME]));
 }
index ed45569..3d77b38 100644 (file)
@@ -47,16 +47,59 @@ static void smc_rx_wake_up(struct sock *sk)
  *   @conn   connection to update
  *   @cons   consumer cursor
  *   @len    number of Bytes consumed
+ *   Returns:
+ *   1 if we should end our receive, 0 otherwise
  */
-static void smc_rx_update_consumer(struct smc_connection *conn,
-                                  union smc_host_cursor cons, size_t len)
+static int smc_rx_update_consumer(struct smc_sock *smc,
+                                 union smc_host_cursor cons, size_t len)
 {
-       smc_curs_add(conn->rmbe_size, &cons, len);
+       struct smc_connection *conn = &smc->conn;
+       struct sock *sk = &smc->sk;
+       bool force = false;
+       int diff, rc = 0;
+
+       smc_curs_add(conn->rmb_desc->len, &cons, len);
+
+       /* did we process urgent data? */
+       if (conn->urg_state == SMC_URG_VALID || conn->urg_rx_skip_pend) {
+               diff = smc_curs_comp(conn->rmb_desc->len, &cons,
+                                    &conn->urg_curs);
+               if (sock_flag(sk, SOCK_URGINLINE)) {
+                       if (diff == 0) {
+                               force = true;
+                               rc = 1;
+                               conn->urg_state = SMC_URG_READ;
+                       }
+               } else {
+                       if (diff == 1) {
+                               /* skip urgent byte */
+                               force = true;
+                               smc_curs_add(conn->rmb_desc->len, &cons, 1);
+                               conn->urg_rx_skip_pend = false;
+                       } else if (diff < -1)
+                               /* we read past urgent byte */
+                               conn->urg_state = SMC_URG_READ;
+               }
+       }
+
        smc_curs_write(&conn->local_tx_ctrl.cons, smc_curs_read(&cons, conn),
                       conn);
+
        /* send consumer cursor update if required */
        /* similar to advertising new TCP rcv_wnd if required */
-       smc_tx_consumer_update(conn);
+       smc_tx_consumer_update(conn, force);
+
+       return rc;
+}
+
+static void smc_rx_update_cons(struct smc_sock *smc, size_t len)
+{
+       struct smc_connection *conn = &smc->conn;
+       union smc_host_cursor cons;
+
+       smc_curs_write(&cons, smc_curs_read(&conn->local_tx_ctrl.cons, conn),
+                      conn);
+       smc_rx_update_consumer(smc, cons, len);
 }
 
 struct smc_spd_priv {
@@ -70,7 +113,6 @@ static void smc_rx_pipe_buf_release(struct pipe_inode_info *pipe,
        struct smc_spd_priv *priv = (struct smc_spd_priv *)buf->private;
        struct smc_sock *smc = priv->smc;
        struct smc_connection *conn;
-       union smc_host_cursor cons;
        struct sock *sk = &smc->sk;
 
        if (sk->sk_state == SMC_CLOSED ||
@@ -79,9 +121,7 @@ static void smc_rx_pipe_buf_release(struct pipe_inode_info *pipe,
                goto out;
        conn = &smc->conn;
        lock_sock(sk);
-       smc_curs_write(&cons, smc_curs_read(&conn->local_tx_ctrl.cons, conn),
-                      conn);
-       smc_rx_update_consumer(conn, cons, priv->len);
+       smc_rx_update_cons(smc, priv->len);
        release_sock(sk);
        if (atomic_sub_and_test(priv->len, &conn->splice_pending))
                smc_rx_wake_up(sk);
@@ -184,6 +224,52 @@ int smc_rx_wait(struct smc_sock *smc, long *timeo,
        return rc;
 }
 
+static int smc_rx_recv_urg(struct smc_sock *smc, struct msghdr *msg, int len,
+                          int flags)
+{
+       struct smc_connection *conn = &smc->conn;
+       union smc_host_cursor cons;
+       struct sock *sk = &smc->sk;
+       int rc = 0;
+
+       if (sock_flag(sk, SOCK_URGINLINE) ||
+           !(conn->urg_state == SMC_URG_VALID) ||
+           conn->urg_state == SMC_URG_READ)
+               return -EINVAL;
+
+       if (conn->urg_state == SMC_URG_VALID) {
+               if (!(flags & MSG_PEEK))
+                       smc->conn.urg_state = SMC_URG_READ;
+               msg->msg_flags |= MSG_OOB;
+               if (len > 0) {
+                       if (!(flags & MSG_TRUNC))
+                               rc = memcpy_to_msg(msg, &conn->urg_rx_byte, 1);
+                       len = 1;
+                       smc_curs_write(&cons,
+                                      smc_curs_read(&conn->local_tx_ctrl.cons,
+                                                    conn),
+                                      conn);
+                       if (smc_curs_diff(conn->rmb_desc->len, &cons,
+                                         &conn->urg_curs) > 1)
+                               conn->urg_rx_skip_pend = true;
+                       /* Urgent Byte was already accounted for, but trigger
+                        * skipping the urgent byte in non-inline case
+                        */
+                       if (!(flags & MSG_PEEK))
+                               smc_rx_update_consumer(smc, cons, 0);
+               } else {
+                       msg->msg_flags |= MSG_TRUNC;
+               }
+
+               return rc ? -EFAULT : len;
+       }
+
+       if (sk->sk_state == SMC_CLOSED || sk->sk_shutdown & RCV_SHUTDOWN)
+               return 0;
+
+       return -EAGAIN;
+}
+
 /* smc_rx_recvmsg - receive data from RMBE
  * @msg:       copy data to receive buffer
  * @pipe:      copy data to pipe if set - indicates splice() call
@@ -209,12 +295,12 @@ int smc_rx_recvmsg(struct smc_sock *smc, struct msghdr *msg,
 
        if (unlikely(flags & MSG_ERRQUEUE))
                return -EINVAL; /* future work for sk.sk_family == AF_SMC */
-       if (flags & MSG_OOB)
-               return -EINVAL; /* future work */
 
        sk = &smc->sk;
        if (sk->sk_state == SMC_LISTEN)
                return -ENOTCONN;
+       if (flags & MSG_OOB)
+               return smc_rx_recv_urg(smc, msg, len, flags);
        timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
        target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
 
@@ -227,6 +313,9 @@ int smc_rx_recvmsg(struct smc_sock *smc, struct msghdr *msg,
 
                if (atomic_read(&conn->bytes_to_rcv))
                        goto copy;
+               else if (conn->urg_state == SMC_URG_VALID)
+                       /* we received a single urgent Byte - skip */
+                       smc_rx_update_cons(smc, 0);
 
                if (sk->sk_shutdown & RCV_SHUTDOWN ||
                    smc_cdc_rxed_any_close_or_senddone(conn) ||
@@ -281,18 +370,22 @@ copy:
                        continue;
                }
 
-               /* not more than what user space asked for */
-               copylen = min_t(size_t, read_remaining, readable);
                smc_curs_write(&cons,
                               smc_curs_read(&conn->local_tx_ctrl.cons, conn),
                               conn);
                /* subsequent splice() calls pick up where previous left */
                if (splbytes)
-                       smc_curs_add(conn->rmbe_size, &cons, splbytes);
+                       smc_curs_add(conn->rmb_desc->len, &cons, splbytes);
+               if (conn->urg_state == SMC_URG_VALID &&
+                   sock_flag(&smc->sk, SOCK_URGINLINE) &&
+                   readable > 1)
+                       readable--;     /* always stop at urgent Byte */
+               /* not more than what user space asked for */
+               copylen = min_t(size_t, read_remaining, readable);
                /* determine chunks where to read from rcvbuf */
                /* either unwrapped case, or 1st chunk of wrapped case */
-               chunk_len = min_t(size_t,
-                                 copylen, conn->rmbe_size - cons.count);
+               chunk_len = min_t(size_t, copylen, conn->rmb_desc->len -
+                                 cons.count);
                chunk_len_sum = chunk_len;
                chunk_off = cons.count;
                smc_rmb_sync_sg_for_cpu(conn);
@@ -331,10 +424,10 @@ copy:
                        /* increased in recv tasklet smc_cdc_msg_rcv() */
                        smp_mb__before_atomic();
                        atomic_sub(copylen, &conn->bytes_to_rcv);
-                       /* guarantee 0 <= bytes_to_rcv <= rmbe_size */
+                       /* guarantee 0 <= bytes_to_rcv <= rmb_desc->len */
                        smp_mb__after_atomic();
-                       if (msg)
-                               smc_rx_update_consumer(conn, cons, copylen);
+                       if (msg && smc_rx_update_consumer(smc, cons, copylen))
+                               goto out;
                }
        } while (read_remaining);
 out:
@@ -346,4 +439,5 @@ void smc_rx_init(struct smc_sock *smc)
 {
        smc->sk.sk_data_ready = smc_rx_wake_up;
        atomic_set(&smc->conn.splice_pending, 0);
+       smc->conn.urg_state = SMC_URG_READ;
 }
index 58dfe0b..cee6664 100644 (file)
@@ -32,7 +32,7 @@
 /***************************** sndbuf producer *******************************/
 
 /* callback implementation for sk.sk_write_space()
- * to wakeup sndbuf producers that blocked with smc_tx_wait_memory().
+ * to wakeup sndbuf producers that blocked with smc_tx_wait().
  * called under sk_socket lock.
  */
 static void smc_tx_write_space(struct sock *sk)
@@ -56,7 +56,7 @@ static void smc_tx_write_space(struct sock *sk)
        }
 }
 
-/* Wakeup sndbuf producers that blocked with smc_tx_wait_memory().
+/* Wakeup sndbuf producers that blocked with smc_tx_wait().
  * Cf. tcp_data_snd_check()=>tcp_check_space()=>tcp_new_space().
  */
 void smc_tx_sndbuf_nonfull(struct smc_sock *smc)
@@ -66,8 +66,10 @@ void smc_tx_sndbuf_nonfull(struct smc_sock *smc)
                smc->sk.sk_write_space(&smc->sk);
 }
 
-/* blocks sndbuf producer until at least one byte of free space available */
-static int smc_tx_wait_memory(struct smc_sock *smc, int flags)
+/* blocks sndbuf producer until at least one byte of free space available
+ * or urgent Byte was consumed
+ */
+static int smc_tx_wait(struct smc_sock *smc, int flags)
 {
        DEFINE_WAIT_FUNC(wait, woken_wake_function);
        struct smc_connection *conn = &smc->conn;
@@ -103,14 +105,15 @@ static int smc_tx_wait_memory(struct smc_sock *smc, int flags)
                        break;
                }
                sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
-               if (atomic_read(&conn->sndbuf_space))
-                       break; /* at least 1 byte of free space available */
+               if (atomic_read(&conn->sndbuf_space) && !conn->urg_tx_pend)
+                       break; /* at least 1 byte of free & no urgent data */
                set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
                sk_wait_event(sk, &timeo,
                              sk->sk_err ||
                              (sk->sk_shutdown & SEND_SHUTDOWN) ||
                              smc_cdc_rxed_any_close(conn) ||
-                             atomic_read(&conn->sndbuf_space),
+                             (atomic_read(&conn->sndbuf_space) &&
+                              !conn->urg_tx_pend),
                              &wait);
        }
        remove_wait_queue(sk_sleep(sk), &wait);
@@ -157,8 +160,11 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len)
                if (smc_cdc_rxed_any_close(conn))
                        return send_done ?: -ECONNRESET;
 
-               if (!atomic_read(&conn->sndbuf_space)) {
-                       rc = smc_tx_wait_memory(smc, msg->msg_flags);
+               if (msg->msg_flags & MSG_OOB)
+                       conn->local_tx_ctrl.prod_flags.urg_data_pending = 1;
+
+               if (!atomic_read(&conn->sndbuf_space) || conn->urg_tx_pend) {
+                       rc = smc_tx_wait(smc, msg->msg_flags);
                        if (rc) {
                                if (send_done)
                                        return send_done;
@@ -168,7 +174,7 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len)
                }
 
                /* initialize variables for 1st iteration of subsequent loop */
-               /* could be just 1 byte, even after smc_tx_wait_memory above */
+               /* could be just 1 byte, even after smc_tx_wait above */
                writespace = atomic_read(&conn->sndbuf_space);
                /* not more than what user space asked for */
                copylen = min_t(size_t, send_remaining, writespace);
@@ -180,8 +186,8 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len)
                tx_cnt_prep = prep.count;
                /* determine chunks where to write into sndbuf */
                /* either unwrapped case, or 1st chunk of wrapped case */
-               chunk_len = min_t(size_t,
-                                 copylen, conn->sndbuf_size - tx_cnt_prep);
+               chunk_len = min_t(size_t, copylen, conn->sndbuf_desc->len -
+                                 tx_cnt_prep);
                chunk_len_sum = chunk_len;
                chunk_off = tx_cnt_prep;
                smc_sndbuf_sync_sg_for_cpu(conn);
@@ -206,21 +212,23 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len)
                }
                smc_sndbuf_sync_sg_for_device(conn);
                /* update cursors */
-               smc_curs_add(conn->sndbuf_size, &prep, copylen);
+               smc_curs_add(conn->sndbuf_desc->len, &prep, copylen);
                smc_curs_write(&conn->tx_curs_prep,
                               smc_curs_read(&prep, conn),
                               conn);
                /* increased in send tasklet smc_cdc_tx_handler() */
                smp_mb__before_atomic();
                atomic_sub(copylen, &conn->sndbuf_space);
-               /* guarantee 0 <= sndbuf_space <= sndbuf_size */
+               /* guarantee 0 <= sndbuf_space <= sndbuf_desc->len */
                smp_mb__after_atomic();
                /* since we just produced more new data into sndbuf,
                 * trigger sndbuf consumer: RDMA write into peer RMBE and CDC
                 */
+               if ((msg->msg_flags & MSG_OOB) && !send_remaining)
+                       conn->urg_tx_pend = true;
                if ((msg->msg_flags & MSG_MORE || smc_tx_is_corked(smc)) &&
                    (atomic_read(&conn->sndbuf_space) >
-                                               (conn->sndbuf_size >> 1)))
+                                               (conn->sndbuf_desc->len >> 1)))
                        /* for a corked socket defer the RDMA writes if there
                         * is still sufficient sndbuf_space available
                         */
@@ -261,7 +269,7 @@ static int smc_tx_rdma_write(struct smc_connection *conn, int peer_rmbe_offset,
        rdma_wr.remote_addr =
                lgr->rtokens[conn->rtoken_idx][SMC_SINGLE_LINK].dma_addr +
                /* RMBE within RMB */
-               ((conn->peer_conn_idx - 1) * conn->peer_rmbe_size) +
+               conn->tx_off +
                /* offset within RMBE */
                peer_rmbe_offset;
        rdma_wr.rkey = lgr->rtokens[conn->rtoken_idx][SMC_SINGLE_LINK].rkey;
@@ -286,7 +294,7 @@ static inline void smc_tx_advance_cursors(struct smc_connection *conn,
        atomic_sub(len, &conn->peer_rmbe_space);
        /* guarantee 0 <= peer_rmbe_space <= peer_rmbe_size */
        smp_mb__after_atomic();
-       smc_curs_add(conn->sndbuf_size, sent, len);
+       smc_curs_add(conn->sndbuf_desc->len, sent, len);
 }
 
 /* sndbuf consumer: prepare all necessary (src&dst) chunks of data transmit;
@@ -299,6 +307,7 @@ static int smc_tx_rdma_writes(struct smc_connection *conn)
        union smc_host_cursor sent, prep, prod, cons;
        struct ib_sge sges[SMC_IB_MAX_SEND_SGE];
        struct smc_link_group *lgr = conn->lgr;
+       struct smc_cdc_producer_flags *pflags;
        int to_send, rmbespace;
        struct smc_link *link;
        dma_addr_t dma_addr;
@@ -309,7 +318,7 @@ static int smc_tx_rdma_writes(struct smc_connection *conn)
        smc_curs_write(&sent, smc_curs_read(&conn->tx_curs_sent, conn), conn);
        smc_curs_write(&prep, smc_curs_read(&conn->tx_curs_prep, conn), conn);
        /* cf. wmem_alloc - (snd_max - snd_una) */
-       to_send = smc_curs_diff(conn->sndbuf_size, &sent, &prep);
+       to_send = smc_curs_diff(conn->sndbuf_desc->len, &sent, &prep);
        if (to_send <= 0)
                return 0;
 
@@ -326,7 +335,8 @@ static int smc_tx_rdma_writes(struct smc_connection *conn)
                       conn);
 
        /* if usable snd_wnd closes ask peer to advertise once it opens again */
-       conn->local_tx_ctrl.prod_flags.write_blocked = (to_send >= rmbespace);
+       pflags = &conn->local_tx_ctrl.prod_flags;
+       pflags->write_blocked = (to_send >= rmbespace);
        /* cf. usable snd_wnd */
        len = min(to_send, rmbespace);
 
@@ -351,12 +361,12 @@ static int smc_tx_rdma_writes(struct smc_connection *conn)
        dst_len_sum = dst_len;
        src_off = sent.count;
        /* dst_len determines the maximum src_len */
-       if (sent.count + dst_len <= conn->sndbuf_size) {
+       if (sent.count + dst_len <= conn->sndbuf_desc->len) {
                /* unwrapped src case: single chunk of entire dst_len */
                src_len = dst_len;
        } else {
                /* wrapped src case: 2 chunks of sum dst_len; start with 1st: */
-               src_len = conn->sndbuf_size - sent.count;
+               src_len = conn->sndbuf_desc->len - sent.count;
        }
        src_len_sum = src_len;
        dma_addr = sg_dma_address(conn->sndbuf_desc->sgt[SMC_SINGLE_LINK].sgl);
@@ -368,8 +378,8 @@ static int smc_tx_rdma_writes(struct smc_connection *conn)
                        sges[srcchunk].lkey = link->roce_pd->local_dma_lkey;
                        num_sges++;
                        src_off += src_len;
-                       if (src_off >= conn->sndbuf_size)
-                               src_off -= conn->sndbuf_size;
+                       if (src_off >= conn->sndbuf_desc->len)
+                               src_off -= conn->sndbuf_desc->len;
                                                /* modulo in send ring */
                        if (src_len_sum == dst_len)
                                break; /* either on 1st or 2nd iteration */
@@ -387,10 +397,12 @@ static int smc_tx_rdma_writes(struct smc_connection *conn)
                dst_len = len - dst_len; /* remainder */
                dst_len_sum += dst_len;
                src_len = min_t(int,
-                               dst_len, conn->sndbuf_size - sent.count);
+                               dst_len, conn->sndbuf_desc->len - sent.count);
                src_len_sum = src_len;
        }
 
+       if (conn->urg_tx_pend && len == to_send)
+               pflags->urg_data_present = 1;
        smc_tx_advance_cursors(conn, &prod, &sent, len);
        /* update connection's cursors with advanced local cursors */
        smc_curs_write(&conn->local_tx_ctrl.prod,
@@ -410,6 +422,7 @@ static int smc_tx_rdma_writes(struct smc_connection *conn)
  */
 int smc_tx_sndbuf_nonempty(struct smc_connection *conn)
 {
+       struct smc_cdc_producer_flags *pflags;
        struct smc_cdc_tx_pend *pend;
        struct smc_wr_buf *wr_buf;
        int rc;
@@ -433,14 +446,21 @@ int smc_tx_sndbuf_nonempty(struct smc_connection *conn)
                goto out_unlock;
        }
 
-       rc = smc_tx_rdma_writes(conn);
-       if (rc) {
-               smc_wr_tx_put_slot(&conn->lgr->lnk[SMC_SINGLE_LINK],
-                                  (struct smc_wr_tx_pend_priv *)pend);
-               goto out_unlock;
+       if (!conn->local_tx_ctrl.prod_flags.urg_data_present) {
+               rc = smc_tx_rdma_writes(conn);
+               if (rc) {
+                       smc_wr_tx_put_slot(&conn->lgr->lnk[SMC_SINGLE_LINK],
+                                          (struct smc_wr_tx_pend_priv *)pend);
+                       goto out_unlock;
+               }
        }
 
        rc = smc_cdc_msg_send(conn, wr_buf, pend);
+       pflags = &conn->local_tx_ctrl.prod_flags;
+       if (!rc && pflags->urg_data_present) {
+               pflags->urg_data_pending = 0;
+               pflags->urg_data_present = 0;
+       }
 
 out_unlock:
        spin_unlock_bh(&conn->send_lock);
@@ -450,7 +470,7 @@ out_unlock:
 /* Wakeup sndbuf consumers from process context
  * since there is more data to transmit
  */
-static void smc_tx_work(struct work_struct *work)
+void smc_tx_work(struct work_struct *work)
 {
        struct smc_connection *conn = container_of(to_delayed_work(work),
                                                   struct smc_connection,
@@ -473,7 +493,7 @@ out:
        release_sock(&smc->sk);
 }
 
-void smc_tx_consumer_update(struct smc_connection *conn)
+void smc_tx_consumer_update(struct smc_connection *conn, bool force)
 {
        union smc_host_cursor cfed, cons;
        int to_confirm;
@@ -484,11 +504,12 @@ void smc_tx_consumer_update(struct smc_connection *conn)
        smc_curs_write(&cfed,
                       smc_curs_read(&conn->rx_curs_confirmed, conn),
                       conn);
-       to_confirm = smc_curs_diff(conn->rmbe_size, &cfed, &cons);
+       to_confirm = smc_curs_diff(conn->rmb_desc->len, &cfed, &cons);
 
        if (conn->local_rx_ctrl.prod_flags.cons_curs_upd_req ||
+           force ||
            ((to_confirm > conn->rmbe_update_limit) &&
-            ((to_confirm > (conn->rmbe_size / 2)) ||
+            ((to_confirm > (conn->rmb_desc->len / 2)) ||
              conn->local_rx_ctrl.prod_flags.write_blocked))) {
                if ((smc_cdc_get_slot_and_msg_send(conn) < 0) &&
                    conn->alert_token_local) { /* connection healthy */
@@ -512,6 +533,4 @@ void smc_tx_consumer_update(struct smc_connection *conn)
 void smc_tx_init(struct smc_sock *smc)
 {
        smc->sk.sk_write_space = smc_tx_write_space;
-       INIT_DELAYED_WORK(&smc->conn.tx_work, smc_tx_work);
-       spin_lock_init(&smc->conn.send_lock);
 }
index 7825596..9d22389 100644 (file)
@@ -24,13 +24,14 @@ static inline int smc_tx_prepared_sends(struct smc_connection *conn)
 
        smc_curs_write(&sent, smc_curs_read(&conn->tx_curs_sent, conn), conn);
        smc_curs_write(&prep, smc_curs_read(&conn->tx_curs_prep, conn), conn);
-       return smc_curs_diff(conn->sndbuf_size, &sent, &prep);
+       return smc_curs_diff(conn->sndbuf_desc->len, &sent, &prep);
 }
 
+void smc_tx_work(struct work_struct *work);
 void smc_tx_init(struct smc_sock *smc);
 int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len);
 int smc_tx_sndbuf_nonempty(struct smc_connection *conn);
 void smc_tx_sndbuf_nonfull(struct smc_sock *smc);
-void smc_tx_consumer_update(struct smc_connection *conn);
+void smc_tx_consumer_update(struct smc_connection *conn, bool force);
 
 #endif /* SMC_TX_H */
index 5c3909c..839e1e1 100644 (file)
@@ -680,7 +680,6 @@ static int decrypt_skb(struct sock *sk, struct sk_buff *skb,
        struct scatterlist *sgin = &sgin_arr[0];
        struct strp_msg *rxm = strp_msg(skb);
        int ret, nsg = ARRAY_SIZE(sgin_arr);
-       char aad_recv[TLS_AAD_SPACE_SIZE];
        struct sk_buff *unused;
 
        ret = skb_copy_bits(skb, rxm->offset + TLS_HEADER_SIZE,
@@ -697,13 +696,13 @@ static int decrypt_skb(struct sock *sk, struct sk_buff *skb,
        }
 
        sg_init_table(sgin, nsg);
-       sg_set_buf(&sgin[0], aad_recv, sizeof(aad_recv));
+       sg_set_buf(&sgin[0], ctx->rx_aad_ciphertext, TLS_AAD_SPACE_SIZE);
 
        nsg = skb_to_sgvec(skb, &sgin[1],
                           rxm->offset + tls_ctx->rx.prepend_size,
                           rxm->full_len - tls_ctx->rx.prepend_size);
 
-       tls_make_aad(aad_recv,
+       tls_make_aad(ctx->rx_aad_ciphertext,
                     rxm->full_len - tls_ctx->rx.overhead_size,
                     tls_ctx->rx.rec_seq,
                     tls_ctx->rx.rec_seq_size,
@@ -802,12 +801,12 @@ int tls_sw_recvmsg(struct sock *sk,
                        if (to_copy <= len && page_count < MAX_SKB_FRAGS &&
                            likely(!(flags & MSG_PEEK)))  {
                                struct scatterlist sgin[MAX_SKB_FRAGS + 1];
-                               char unused[21];
                                int pages = 0;
 
                                zc = true;
                                sg_init_table(sgin, MAX_SKB_FRAGS + 1);
-                               sg_set_buf(&sgin[0], unused, 13);
+                               sg_set_buf(&sgin[0], ctx->rx_aad_plaintext,
+                                          TLS_AAD_SPACE_SIZE);
 
                                err = zerocopy_from_iter(sk, &msg->msg_iter,
                                                         to_copy, &pages,
index c0fd8a8..5fe35aa 100644 (file)
@@ -725,6 +725,10 @@ int wiphy_register(struct wiphy *wiphy)
                    (!rdev->ops->set_pmk || !rdev->ops->del_pmk)))
                return -EINVAL;
 
+       if (WARN_ON(!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
+                   rdev->ops->update_connect_params))
+               return -EINVAL;
+
        if (wiphy->addresses)
                memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
 
index a052693..bc40a78 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
  */
 
 #include <linux/if.h>
@@ -423,6 +424,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN },
        [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG },
        [NL80211_ATTR_EXTERNAL_AUTH_SUPPORT] = { .type = NLA_FLAG },
+
+       [NL80211_ATTR_TXQ_LIMIT] = { .type = NLA_U32 },
+       [NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 },
+       [NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 },
 };
 
 /* policy for the key attributes */
@@ -645,7 +650,43 @@ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
        return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd);
 }
 
-static int nl80211_msg_put_channel(struct sk_buff *msg,
+static int nl80211_msg_put_wmm_rules(struct sk_buff *msg,
+                                    const struct ieee80211_reg_rule *rule)
+{
+       int j;
+       struct nlattr *nl_wmm_rules =
+               nla_nest_start(msg, NL80211_FREQUENCY_ATTR_WMM);
+
+       if (!nl_wmm_rules)
+               goto nla_put_failure;
+
+       for (j = 0; j < IEEE80211_NUM_ACS; j++) {
+               struct nlattr *nl_wmm_rule = nla_nest_start(msg, j);
+
+               if (!nl_wmm_rule)
+                       goto nla_put_failure;
+
+               if (nla_put_u16(msg, NL80211_WMMR_CW_MIN,
+                               rule->wmm_rule->client[j].cw_min) ||
+                   nla_put_u16(msg, NL80211_WMMR_CW_MAX,
+                               rule->wmm_rule->client[j].cw_max) ||
+                   nla_put_u8(msg, NL80211_WMMR_AIFSN,
+                              rule->wmm_rule->client[j].aifsn) ||
+                   nla_put_u8(msg, NL80211_WMMR_TXOP,
+                              rule->wmm_rule->client[j].cot))
+                       goto nla_put_failure;
+
+               nla_nest_end(msg, nl_wmm_rule);
+       }
+       nla_nest_end(msg, nl_wmm_rules);
+
+       return 0;
+
+nla_put_failure:
+       return -ENOBUFS;
+}
+
+static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
                                   struct ieee80211_channel *chan,
                                   bool large)
 {
@@ -721,12 +762,55 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
                        DBM_TO_MBM(chan->max_power)))
                goto nla_put_failure;
 
+       if (large) {
+               const struct ieee80211_reg_rule *rule =
+                       freq_reg_info(wiphy, chan->center_freq);
+
+               if (!IS_ERR(rule) && rule->wmm_rule) {
+                       if (nl80211_msg_put_wmm_rules(msg, rule))
+                               goto nla_put_failure;
+               }
+       }
+
        return 0;
 
  nla_put_failure:
        return -ENOBUFS;
 }
 
+static bool nl80211_put_txq_stats(struct sk_buff *msg,
+                                 struct cfg80211_txq_stats *txqstats,
+                                 int attrtype)
+{
+       struct nlattr *txqattr;
+
+#define PUT_TXQVAL_U32(attr, memb) do {                                          \
+       if (txqstats->filled & BIT(NL80211_TXQ_STATS_ ## attr) &&         \
+           nla_put_u32(msg, NL80211_TXQ_STATS_ ## attr, txqstats->memb)) \
+               return false;                                             \
+       } while (0)
+
+       txqattr = nla_nest_start(msg, attrtype);
+       if (!txqattr)
+               return false;
+
+       PUT_TXQVAL_U32(BACKLOG_BYTES, backlog_bytes);
+       PUT_TXQVAL_U32(BACKLOG_PACKETS, backlog_packets);
+       PUT_TXQVAL_U32(FLOWS, flows);
+       PUT_TXQVAL_U32(DROPS, drops);
+       PUT_TXQVAL_U32(ECN_MARKS, ecn_marks);
+       PUT_TXQVAL_U32(OVERLIMIT, overlimit);
+       PUT_TXQVAL_U32(OVERMEMORY, overmemory);
+       PUT_TXQVAL_U32(COLLISIONS, collisions);
+       PUT_TXQVAL_U32(TX_BYTES, tx_bytes);
+       PUT_TXQVAL_U32(TX_PACKETS, tx_packets);
+       PUT_TXQVAL_U32(MAX_FLOWS, max_flows);
+       nla_nest_end(msg, txqattr);
+
+#undef PUT_TXQVAL_U32
+       return true;
+}
+
 /* netlink command implementations */
 
 struct key_parse {
@@ -1631,7 +1715,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                                        chan = &sband->channels[i];
 
                                        if (nl80211_msg_put_channel(
-                                                       msg, chan,
+                                                       msg, &rdev->wiphy, chan,
                                                        state->split))
                                                goto nla_put_failure;
 
@@ -1926,6 +2010,28 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                                rdev->wiphy.nan_supported_bands))
                        goto nla_put_failure;
 
+               if (wiphy_ext_feature_isset(&rdev->wiphy,
+                                           NL80211_EXT_FEATURE_TXQS)) {
+                       struct cfg80211_txq_stats txqstats = {};
+                       int res;
+
+                       res = rdev_get_txq_stats(rdev, NULL, &txqstats);
+                       if (!res &&
+                           !nl80211_put_txq_stats(msg, &txqstats,
+                                                  NL80211_ATTR_TXQ_STATS))
+                               goto nla_put_failure;
+
+                       if (nla_put_u32(msg, NL80211_ATTR_TXQ_LIMIT,
+                                       rdev->wiphy.txq_limit))
+                               goto nla_put_failure;
+                       if (nla_put_u32(msg, NL80211_ATTR_TXQ_MEMORY_LIMIT,
+                                       rdev->wiphy.txq_memory_limit))
+                               goto nla_put_failure;
+                       if (nla_put_u32(msg, NL80211_ATTR_TXQ_QUANTUM,
+                                       rdev->wiphy.txq_quantum))
+                               goto nla_put_failure;
+               }
+
                /* done */
                state->split_start = 0;
                break;
@@ -2303,6 +2409,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        u8 retry_short = 0, retry_long = 0;
        u32 frag_threshold = 0, rts_threshold = 0;
        u8 coverage_class = 0;
+       u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0;
 
        ASSERT_RTNL();
 
@@ -2509,10 +2616,38 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                changed |= WIPHY_PARAM_DYN_ACK;
        }
 
+       if (info->attrs[NL80211_ATTR_TXQ_LIMIT]) {
+               if (!wiphy_ext_feature_isset(&rdev->wiphy,
+                                            NL80211_EXT_FEATURE_TXQS))
+                       return -EOPNOTSUPP;
+               txq_limit = nla_get_u32(
+                       info->attrs[NL80211_ATTR_TXQ_LIMIT]);
+               changed |= WIPHY_PARAM_TXQ_LIMIT;
+       }
+
+       if (info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]) {
+               if (!wiphy_ext_feature_isset(&rdev->wiphy,
+                                            NL80211_EXT_FEATURE_TXQS))
+                       return -EOPNOTSUPP;
+               txq_memory_limit = nla_get_u32(
+                       info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]);
+               changed |= WIPHY_PARAM_TXQ_MEMORY_LIMIT;
+       }
+
+       if (info->attrs[NL80211_ATTR_TXQ_QUANTUM]) {
+               if (!wiphy_ext_feature_isset(&rdev->wiphy,
+                                            NL80211_EXT_FEATURE_TXQS))
+                       return -EOPNOTSUPP;
+               txq_quantum = nla_get_u32(
+                       info->attrs[NL80211_ATTR_TXQ_QUANTUM]);
+               changed |= WIPHY_PARAM_TXQ_QUANTUM;
+       }
+
        if (changed) {
                u8 old_retry_short, old_retry_long;
                u32 old_frag_threshold, old_rts_threshold;
                u8 old_coverage_class;
+               u32 old_txq_limit, old_txq_memory_limit, old_txq_quantum;
 
                if (!rdev->ops->set_wiphy_params)
                        return -EOPNOTSUPP;
@@ -2522,6 +2657,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                old_frag_threshold = rdev->wiphy.frag_threshold;
                old_rts_threshold = rdev->wiphy.rts_threshold;
                old_coverage_class = rdev->wiphy.coverage_class;
+               old_txq_limit = rdev->wiphy.txq_limit;
+               old_txq_memory_limit = rdev->wiphy.txq_memory_limit;
+               old_txq_quantum = rdev->wiphy.txq_quantum;
 
                if (changed & WIPHY_PARAM_RETRY_SHORT)
                        rdev->wiphy.retry_short = retry_short;
@@ -2533,6 +2671,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        rdev->wiphy.rts_threshold = rts_threshold;
                if (changed & WIPHY_PARAM_COVERAGE_CLASS)
                        rdev->wiphy.coverage_class = coverage_class;
+               if (changed & WIPHY_PARAM_TXQ_LIMIT)
+                       rdev->wiphy.txq_limit = txq_limit;
+               if (changed & WIPHY_PARAM_TXQ_MEMORY_LIMIT)
+                       rdev->wiphy.txq_memory_limit = txq_memory_limit;
+               if (changed & WIPHY_PARAM_TXQ_QUANTUM)
+                       rdev->wiphy.txq_quantum = txq_quantum;
 
                result = rdev_set_wiphy_params(rdev, changed);
                if (result) {
@@ -2541,6 +2685,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        rdev->wiphy.frag_threshold = old_frag_threshold;
                        rdev->wiphy.rts_threshold = old_rts_threshold;
                        rdev->wiphy.coverage_class = old_coverage_class;
+                       rdev->wiphy.txq_limit = old_txq_limit;
+                       rdev->wiphy.txq_memory_limit = old_txq_memory_limit;
+                       rdev->wiphy.txq_quantum = old_txq_quantum;
                        return result;
                }
        }
@@ -2662,6 +2809,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
        }
        wdev_unlock(wdev);
 
+       if (rdev->ops->get_txq_stats) {
+               struct cfg80211_txq_stats txqstats = {};
+               int ret = rdev_get_txq_stats(rdev, wdev, &txqstats);
+
+               if (ret == 0 &&
+                   !nl80211_put_txq_stats(msg, &txqstats,
+                                          NL80211_ATTR_TXQ_STATS))
+                       goto nla_put_failure;
+       }
+
        genlmsg_end(msg, hdr);
        return 0;
 
@@ -4494,11 +4651,14 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
        PUT_SINFO_U64(BEACON_RX, rx_beacon);
        PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8);
        PUT_SINFO(ACK_SIGNAL, ack_signal, u8);
+       if (wiphy_ext_feature_isset(&rdev->wiphy,
+                                   NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT))
+               PUT_SINFO(DATA_ACK_SIGNAL_AVG, avg_ack_signal, s8);
 
 #undef PUT_SINFO
 #undef PUT_SINFO_U64
 
-       if (sinfo->filled & BIT(NL80211_STA_INFO_TID_STATS)) {
+       if (sinfo->pertid) {
                struct nlattr *tidsattr;
                int tid;
 
@@ -4532,6 +4692,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
                        PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed);
 
 #undef PUT_TIDVAL_U64
+                       if ((tidstats->filled &
+                            BIT(NL80211_TID_STATS_TXQ_STATS)) &&
+                           !nl80211_put_txq_stats(msg, &tidstats->txq_stats,
+                                                  NL80211_TID_STATS_TXQ_STATS))
+                               goto nla_put_failure;
+
                        nla_nest_end(msg, tidattr);
                }
 
@@ -4545,10 +4711,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
                    sinfo->assoc_req_ies))
                goto nla_put_failure;
 
+       cfg80211_sinfo_release_content(sinfo);
        genlmsg_end(msg, hdr);
        return 0;
 
  nla_put_failure:
+       cfg80211_sinfo_release_content(sinfo);
        genlmsg_cancel(msg, hdr);
        return -EMSGSIZE;
 }
@@ -4630,8 +4798,10 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
                return err;
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg)
+       if (!msg) {
+               cfg80211_sinfo_release_content(&sinfo);
                return -ENOMEM;
+       }
 
        if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
                                 info->snd_portid, info->snd_seq, 0,
@@ -7930,7 +8100,15 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
 
        wdev_lock(wdev);
        spin_lock_bh(&rdev->bss_lock);
-       cfg80211_bss_expire(rdev);
+
+       /*
+        * dump_scan will be called multiple times to break up the scan results
+        * into multiple messages.  It is unlikely that any more bss-es will be
+        * expired after the first call, so only call only call this on the
+        * first dump_scan invocation.
+        */
+       if (start == 0)
+               cfg80211_bss_expire(rdev);
 
        cb->seq = rdev->bss_generation;
 
@@ -8336,6 +8514,10 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        const u8 *bssid, *ssid;
        int err, ssid_len = 0;
 
+       if (dev->ieee80211_ptr->conn_owner_nlportid &&
+           dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid)
+               return -EPERM;
+
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
 
@@ -8458,6 +8640,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
        u16 reason_code;
        bool local_state_change;
 
+       if (dev->ieee80211_ptr->conn_owner_nlportid &&
+           dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid)
+               return -EPERM;
+
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
 
@@ -8505,6 +8691,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
        u16 reason_code;
        bool local_state_change;
 
+       if (dev->ieee80211_ptr->conn_owner_nlportid &&
+           dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid)
+               return -EPERM;
+
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
 
@@ -9251,6 +9441,8 @@ static int nl80211_update_connect_params(struct sk_buff *skb,
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       bool fils_sk_offload;
+       u32 auth_type;
        u32 changed = 0;
        int ret;
 
@@ -9265,6 +9457,56 @@ static int nl80211_update_connect_params(struct sk_buff *skb,
                changed |= UPDATE_ASSOC_IES;
        }
 
+       fils_sk_offload = wiphy_ext_feature_isset(&rdev->wiphy,
+                                                 NL80211_EXT_FEATURE_FILS_SK_OFFLOAD);
+
+       /*
+        * when driver supports fils-sk offload all attributes must be
+        * provided. So the else covers "fils-sk-not-all" and
+        * "no-fils-sk-any".
+        */
+       if (fils_sk_offload &&
+           info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] &&
+           info->attrs[NL80211_ATTR_FILS_ERP_REALM] &&
+           info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] &&
+           info->attrs[NL80211_ATTR_FILS_ERP_RRK]) {
+               connect.fils_erp_username =
+                       nla_data(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]);
+               connect.fils_erp_username_len =
+                       nla_len(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]);
+               connect.fils_erp_realm =
+                       nla_data(info->attrs[NL80211_ATTR_FILS_ERP_REALM]);
+               connect.fils_erp_realm_len =
+                       nla_len(info->attrs[NL80211_ATTR_FILS_ERP_REALM]);
+               connect.fils_erp_next_seq_num =
+                       nla_get_u16(
+                          info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]);
+               connect.fils_erp_rrk =
+                       nla_data(info->attrs[NL80211_ATTR_FILS_ERP_RRK]);
+               connect.fils_erp_rrk_len =
+                       nla_len(info->attrs[NL80211_ATTR_FILS_ERP_RRK]);
+               changed |= UPDATE_FILS_ERP_INFO;
+       } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] ||
+                  info->attrs[NL80211_ATTR_FILS_ERP_REALM] ||
+                  info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] ||
+                  info->attrs[NL80211_ATTR_FILS_ERP_RRK]) {
+               return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
+               auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+               if (!nl80211_valid_auth_type(rdev, auth_type,
+                                            NL80211_CMD_CONNECT))
+                       return -EINVAL;
+
+               if (auth_type == NL80211_AUTHTYPE_FILS_SK &&
+                   fils_sk_offload && !(changed & UPDATE_FILS_ERP_INFO))
+                       return -EINVAL;
+
+               connect.auth_type = auth_type;
+               changed |= UPDATE_AUTH_TYPE;
+       }
+
        wdev_lock(dev->ieee80211_ptr);
        if (!wdev->current_bss)
                ret = -ENOLINK;
@@ -9282,6 +9524,10 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
        u16 reason;
        int ret;
 
+       if (dev->ieee80211_ptr->conn_owner_nlportid &&
+           dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid)
+               return -EPERM;
+
        if (!info->attrs[NL80211_ATTR_REASON_CODE])
                reason = WLAN_REASON_DEAUTH_LEAVING;
        else
@@ -14028,8 +14274,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
        void *hdr;
 
        msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len +
-                       cr->fils_kek_len + cr->pmk_len +
-                       (cr->pmkid ? WLAN_PMKID_LEN : 0), gfp);
+                       cr->fils.kek_len + cr->fils.pmk_len +
+                       (cr->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp);
        if (!msg)
                return;
 
@@ -14055,17 +14301,17 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
            (cr->resp_ie &&
             nla_put(msg, NL80211_ATTR_RESP_IE, cr->resp_ie_len,
                     cr->resp_ie)) ||
-           (cr->update_erp_next_seq_num &&
+           (cr->fils.update_erp_next_seq_num &&
             nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
-                        cr->fils_erp_next_seq_num)) ||
+                        cr->fils.erp_next_seq_num)) ||
            (cr->status == WLAN_STATUS_SUCCESS &&
-            ((cr->fils_kek &&
-              nla_put(msg, NL80211_ATTR_FILS_KEK, cr->fils_kek_len,
-                      cr->fils_kek)) ||
-             (cr->pmk &&
-              nla_put(msg, NL80211_ATTR_PMK, cr->pmk_len, cr->pmk)) ||
-             (cr->pmkid &&
-              nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->pmkid)))))
+            ((cr->fils.kek &&
+              nla_put(msg, NL80211_ATTR_FILS_KEK, cr->fils.kek_len,
+                      cr->fils.kek)) ||
+             (cr->fils.pmk &&
+              nla_put(msg, NL80211_ATTR_PMK, cr->fils.pmk_len, cr->fils.pmk)) ||
+             (cr->fils.pmkid &&
+              nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->fils.pmkid)))))
                goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
@@ -14086,7 +14332,9 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
        void *hdr;
        const u8 *bssid = info->bss ? info->bss->bssid : info->bssid;
 
-       msg = nlmsg_new(100 + info->req_ie_len + info->resp_ie_len, gfp);
+       msg = nlmsg_new(100 + info->req_ie_len + info->resp_ie_len +
+                       info->fils.kek_len + info->fils.pmk_len +
+                       (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp);
        if (!msg)
                return;
 
@@ -14104,7 +14352,17 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
                     info->req_ie)) ||
            (info->resp_ie &&
             nla_put(msg, NL80211_ATTR_RESP_IE, info->resp_ie_len,
-                    info->resp_ie)))
+                    info->resp_ie)) ||
+           (info->fils.update_erp_next_seq_num &&
+            nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+                        info->fils.erp_next_seq_num)) ||
+           (info->fils.kek &&
+            nla_put(msg, NL80211_ATTR_FILS_KEK, info->fils.kek_len,
+                    info->fils.kek)) ||
+           (info->fils.pmk &&
+            nla_put(msg, NL80211_ATTR_PMK, info->fils.pmk_len, info->fils.pmk)) ||
+           (info->fils.pmkid &&
+            nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, info->fils.pmkid)))
                goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
@@ -14321,7 +14579,8 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
        if (!nl_freq)
                goto nla_put_failure;
-       if (nl80211_msg_put_channel(msg, channel_before, false))
+
+       if (nl80211_msg_put_channel(msg, wiphy, channel_before, false))
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
@@ -14329,7 +14588,8 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
        if (!nl_freq)
                goto nla_put_failure;
-       if (nl80211_msg_put_channel(msg, channel_after, false))
+
+       if (nl80211_msg_put_channel(msg, wiphy, channel_after, false))
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
@@ -14456,8 +14716,10 @@ void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr,
        trace_cfg80211_del_sta(dev, mac_addr);
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
-       if (!msg)
+       if (!msg) {
+               cfg80211_sinfo_release_content(sinfo);
                return;
+       }
 
        if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
                                 rdev, dev, mac_addr, sinfo) < 0) {
index 87479a5..364f5d6 100644 (file)
@@ -586,6 +586,18 @@ rdev_set_multicast_to_unicast(struct cfg80211_registered_device *rdev,
        return ret;
 }
 
+static inline int
+rdev_get_txq_stats(struct cfg80211_registered_device *rdev,
+                  struct wireless_dev *wdev,
+                  struct cfg80211_txq_stats *txqstats)
+{
+       int ret;
+       trace_rdev_get_txq_stats(&rdev->wiphy, wdev);
+       ret = rdev->ops->get_txq_stats(&rdev->wiphy, wdev, txqstats);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
+
 static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev)
 {
        trace_rdev_rfkill_poll(&rdev->wiphy);
index ac3e12c..e55099b 100644 (file)
@@ -1653,7 +1653,7 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
        case NL80211_REGDOM_SET_BY_DRIVER:
                return "driver";
        case NL80211_REGDOM_SET_BY_COUNTRY_IE:
-               return "country IE";
+               return "country element";
        default:
                WARN_ON(1);
                return "bug";
@@ -2619,7 +2619,7 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
                 * This doesn't happen yet, not sure we
                 * ever want to support it for this case.
                 */
-               WARN_ONCE(1, "Unexpected intersection for country IEs");
+               WARN_ONCE(1, "Unexpected intersection for country elements");
                return REG_REQ_IGNORE;
        }
 
@@ -2769,6 +2769,21 @@ out_free:
        reg_free_request(reg_request);
 }
 
+static void notify_self_managed_wiphys(struct regulatory_request *request)
+{
+       struct cfg80211_registered_device *rdev;
+       struct wiphy *wiphy;
+
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               wiphy = &rdev->wiphy;
+               if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+                   request->initiator == NL80211_REGDOM_SET_BY_USER &&
+                   request->user_reg_hint_type ==
+                               NL80211_USER_REG_HINT_CELL_BASE)
+                       reg_call_notifier(wiphy, request);
+       }
+}
+
 static bool reg_only_self_managed_wiphys(void)
 {
        struct cfg80211_registered_device *rdev;
@@ -2820,6 +2835,7 @@ static void reg_process_pending_hints(void)
 
        spin_unlock(&reg_requests_lock);
 
+       notify_self_managed_wiphys(reg_request);
        if (reg_only_self_managed_wiphys()) {
                reg_free_request(reg_request);
                return;
@@ -3384,7 +3400,7 @@ bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region)
        case NL80211_DFS_JP:
                return true;
        default:
-               pr_debug("Ignoring uknown DFS master region: %d\n", dfs_region);
+               pr_debug("Ignoring unknown DFS master region: %d\n", dfs_region);
                return false;
        }
 }
@@ -3699,17 +3715,26 @@ EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl);
 
 void wiphy_regulatory_register(struct wiphy *wiphy)
 {
-       struct regulatory_request *lr;
+       struct regulatory_request *lr = get_last_request();
 
-       /* self-managed devices ignore external hints */
-       if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+       /* self-managed devices ignore beacon hints and country IE */
+       if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
                wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
                                           REGULATORY_COUNTRY_IE_IGNORE;
 
+               /*
+                * The last request may have been received before this
+                * registration call. Call the driver notifier if
+                * initiator is USER and user type is CELL_BASE.
+                */
+               if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
+                   lr->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE)
+                       reg_call_notifier(wiphy, lr);
+       }
+
        if (!reg_dev_ignore_cell_hint(wiphy))
                reg_num_devs_support_basehint++;
 
-       lr = get_last_request();
        wiphy_update_regulatory(wiphy, lr->initiator);
        wiphy_all_share_dfs_chan_state(wiphy);
 }
index 5df6b33..d536b07 100644 (file)
@@ -803,8 +803,8 @@ void cfg80211_connect_done(struct net_device *dev,
 
        ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) +
                     params->req_ie_len + params->resp_ie_len +
-                    params->fils_kek_len + params->pmk_len +
-                    (params->pmkid ? WLAN_PMKID_LEN : 0), gfp);
+                    params->fils.kek_len + params->fils.pmk_len +
+                    (params->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp);
        if (!ev) {
                cfg80211_put_bss(wdev->wiphy, params->bss);
                return;
@@ -831,27 +831,29 @@ void cfg80211_connect_done(struct net_device *dev,
                       params->resp_ie_len);
                next += params->resp_ie_len;
        }
-       if (params->fils_kek_len) {
-               ev->cr.fils_kek = next;
-               ev->cr.fils_kek_len = params->fils_kek_len;
-               memcpy((void *)ev->cr.fils_kek, params->fils_kek,
-                      params->fils_kek_len);
-               next += params->fils_kek_len;
+       if (params->fils.kek_len) {
+               ev->cr.fils.kek = next;
+               ev->cr.fils.kek_len = params->fils.kek_len;
+               memcpy((void *)ev->cr.fils.kek, params->fils.kek,
+                      params->fils.kek_len);
+               next += params->fils.kek_len;
        }
-       if (params->pmk_len) {
-               ev->cr.pmk = next;
-               ev->cr.pmk_len = params->pmk_len;
-               memcpy((void *)ev->cr.pmk, params->pmk, params->pmk_len);
-               next += params->pmk_len;
+       if (params->fils.pmk_len) {
+               ev->cr.fils.pmk = next;
+               ev->cr.fils.pmk_len = params->fils.pmk_len;
+               memcpy((void *)ev->cr.fils.pmk, params->fils.pmk,
+                      params->fils.pmk_len);
+               next += params->fils.pmk_len;
        }
-       if (params->pmkid) {
-               ev->cr.pmkid = next;
-               memcpy((void *)ev->cr.pmkid, params->pmkid, WLAN_PMKID_LEN);
+       if (params->fils.pmkid) {
+               ev->cr.fils.pmkid = next;
+               memcpy((void *)ev->cr.fils.pmkid, params->fils.pmkid,
+                      WLAN_PMKID_LEN);
                next += WLAN_PMKID_LEN;
        }
-       ev->cr.update_erp_next_seq_num = params->update_erp_next_seq_num;
-       if (params->update_erp_next_seq_num)
-               ev->cr.fils_erp_next_seq_num = params->fils_erp_next_seq_num;
+       ev->cr.fils.update_erp_next_seq_num = params->fils.update_erp_next_seq_num;
+       if (params->fils.update_erp_next_seq_num)
+               ev->cr.fils.erp_next_seq_num = params->fils.erp_next_seq_num;
        if (params->bss)
                cfg80211_hold_bss(bss_from_pub(params->bss));
        ev->cr.bss = params->bss;
@@ -930,6 +932,7 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info,
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_event *ev;
        unsigned long flags;
+       u8 *next;
 
        if (!info->bss) {
                info->bss = cfg80211_get_bss(wdev->wiphy, info->channel,
@@ -942,19 +945,52 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info,
        if (WARN_ON(!info->bss))
                return;
 
-       ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len, gfp);
+       ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len +
+                    info->fils.kek_len + info->fils.pmk_len +
+                    (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp);
        if (!ev) {
                cfg80211_put_bss(wdev->wiphy, info->bss);
                return;
        }
 
        ev->type = EVENT_ROAMED;
-       ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
-       ev->rm.req_ie_len = info->req_ie_len;
-       memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len);
-       ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + info->req_ie_len;
-       ev->rm.resp_ie_len = info->resp_ie_len;
-       memcpy((void *)ev->rm.resp_ie, info->resp_ie, info->resp_ie_len);
+       next = ((u8 *)ev) + sizeof(*ev);
+       if (info->req_ie_len) {
+               ev->rm.req_ie = next;
+               ev->rm.req_ie_len = info->req_ie_len;
+               memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len);
+               next += info->req_ie_len;
+       }
+       if (info->resp_ie_len) {
+               ev->rm.resp_ie = next;
+               ev->rm.resp_ie_len = info->resp_ie_len;
+               memcpy((void *)ev->rm.resp_ie, info->resp_ie,
+                      info->resp_ie_len);
+               next += info->resp_ie_len;
+       }
+       if (info->fils.kek_len) {
+               ev->rm.fils.kek = next;
+               ev->rm.fils.kek_len = info->fils.kek_len;
+               memcpy((void *)ev->rm.fils.kek, info->fils.kek,
+                      info->fils.kek_len);
+               next += info->fils.kek_len;
+       }
+       if (info->fils.pmk_len) {
+               ev->rm.fils.pmk = next;
+               ev->rm.fils.pmk_len = info->fils.pmk_len;
+               memcpy((void *)ev->rm.fils.pmk, info->fils.pmk,
+                      info->fils.pmk_len);
+               next += info->fils.pmk_len;
+       }
+       if (info->fils.pmkid) {
+               ev->rm.fils.pmkid = next;
+               memcpy((void *)ev->rm.fils.pmkid, info->fils.pmkid,
+                      WLAN_PMKID_LEN);
+               next += WLAN_PMKID_LEN;
+       }
+       ev->rm.fils.update_erp_next_seq_num = info->fils.update_erp_next_seq_num;
+       if (info->fils.update_erp_next_seq_num)
+               ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num;
        ev->rm.bss = info->bss;
 
        spin_lock_irqsave(&wdev->event_lock, flags);
index 55fb279..2b417a2 100644 (file)
@@ -3243,6 +3243,20 @@ TRACE_EVENT(rdev_set_multicast_to_unicast,
                  WIPHY_PR_ARG, NETDEV_PR_ARG,
                  BOOL_TO_STR(__entry->enabled))
 );
+
+TRACE_EVENT(rdev_get_txq_stats,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+       TP_ARGS(wiphy, wdev),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               WDEV_ENTRY
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WDEV_ASSIGN;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
+);
 #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH
index d112e9a..b5bb1c3 100644 (file)
@@ -1787,6 +1787,17 @@ bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
        return false;
 }
 
+int cfg80211_sinfo_alloc_tid_stats(struct station_info *sinfo, gfp_t gfp)
+{
+       sinfo->pertid = kcalloc(sizeof(*(sinfo->pertid)),
+                               IEEE80211_NUM_TIDS + 1, gfp);
+       if (!sinfo->pertid)
+               return -ENOMEM;
+
+       return 0;
+}
+EXPORT_SYMBOL(cfg80211_sinfo_alloc_tid_stats);
+
 /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
 /* Ethernet-II snap header (RFC1042 for most EtherTypes) */
 const unsigned char rfc1042_header[] __aligned(2) =
index 881dfde..2b47a1d 100644 (file)
@@ -209,7 +209,7 @@ int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
        if ((addr + size) < addr)
                return -EINVAL;
 
-       nframes = size / frame_size;
+       nframes = (unsigned int)div_u64(size, frame_size);
        if (nframes == 0 || nframes > UINT_MAX)
                return -EINVAL;
 
index 8e0c7fb..62a99ab 100644 (file)
@@ -1,4 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
+
+BPF_SAMPLES_PATH ?= $(abspath $(srctree)/$(src))
+TOOLS_PATH := $(BPF_SAMPLES_PATH)/../../tools
+
 # List of programs to build
 hostprogs-y := test_lru_dist
 hostprogs-y += sock_example
@@ -46,60 +50,61 @@ hostprogs-y += syscall_tp
 hostprogs-y += cpustat
 hostprogs-y += xdp_adjust_tail
 hostprogs-y += xdpsock
+hostprogs-y += xdp_fwd
 
 # Libbpf dependencies
-LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o
+LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a
+
 CGROUP_HELPERS := ../../tools/testing/selftests/bpf/cgroup_helpers.o
 TRACE_HELPERS := ../../tools/testing/selftests/bpf/trace_helpers.o
 
-test_lru_dist-objs := test_lru_dist.o $(LIBBPF)
-sock_example-objs := sock_example.o $(LIBBPF)
-fds_example-objs := bpf_load.o $(LIBBPF) fds_example.o
-sockex1-objs := bpf_load.o $(LIBBPF) sockex1_user.o
-sockex2-objs := bpf_load.o $(LIBBPF) sockex2_user.o
-sockex3-objs := bpf_load.o $(LIBBPF) sockex3_user.o
-tracex1-objs := bpf_load.o $(LIBBPF) tracex1_user.o
-tracex2-objs := bpf_load.o $(LIBBPF) tracex2_user.o
-tracex3-objs := bpf_load.o $(LIBBPF) tracex3_user.o
-tracex4-objs := bpf_load.o $(LIBBPF) tracex4_user.o
-tracex5-objs := bpf_load.o $(LIBBPF) tracex5_user.o
-tracex6-objs := bpf_load.o $(LIBBPF) tracex6_user.o
-tracex7-objs := bpf_load.o $(LIBBPF) tracex7_user.o
-load_sock_ops-objs := bpf_load.o $(LIBBPF) load_sock_ops.o
-test_probe_write_user-objs := bpf_load.o $(LIBBPF) test_probe_write_user_user.o
-trace_output-objs := bpf_load.o $(LIBBPF) trace_output_user.o $(TRACE_HELPERS)
-lathist-objs := bpf_load.o $(LIBBPF) lathist_user.o
-offwaketime-objs := bpf_load.o $(LIBBPF) offwaketime_user.o $(TRACE_HELPERS)
-spintest-objs := bpf_load.o $(LIBBPF) spintest_user.o $(TRACE_HELPERS)
-map_perf_test-objs := bpf_load.o $(LIBBPF) map_perf_test_user.o
-test_overhead-objs := bpf_load.o $(LIBBPF) test_overhead_user.o
-test_cgrp2_array_pin-objs := $(LIBBPF) test_cgrp2_array_pin.o
-test_cgrp2_attach-objs := $(LIBBPF) test_cgrp2_attach.o
-test_cgrp2_attach2-objs := $(LIBBPF) test_cgrp2_attach2.o $(CGROUP_HELPERS)
-test_cgrp2_sock-objs := $(LIBBPF) test_cgrp2_sock.o
-test_cgrp2_sock2-objs := bpf_load.o $(LIBBPF) test_cgrp2_sock2.o
-xdp1-objs := bpf_load.o $(LIBBPF) xdp1_user.o
+fds_example-objs := bpf_load.o fds_example.o
+sockex1-objs := bpf_load.o sockex1_user.o
+sockex2-objs := bpf_load.o sockex2_user.o
+sockex3-objs := bpf_load.o sockex3_user.o
+tracex1-objs := bpf_load.o tracex1_user.o
+tracex2-objs := bpf_load.o tracex2_user.o
+tracex3-objs := bpf_load.o tracex3_user.o
+tracex4-objs := bpf_load.o tracex4_user.o
+tracex5-objs := bpf_load.o tracex5_user.o
+tracex6-objs := bpf_load.o tracex6_user.o
+tracex7-objs := bpf_load.o tracex7_user.o
+load_sock_ops-objs := bpf_load.o load_sock_ops.o
+test_probe_write_user-objs := bpf_load.o test_probe_write_user_user.o
+trace_output-objs := bpf_load.o trace_output_user.o $(TRACE_HELPERS)
+lathist-objs := bpf_load.o lathist_user.o
+offwaketime-objs := bpf_load.o offwaketime_user.o $(TRACE_HELPERS)
+spintest-objs := bpf_load.o spintest_user.o $(TRACE_HELPERS)
+map_perf_test-objs := bpf_load.o map_perf_test_user.o
+test_overhead-objs := bpf_load.o test_overhead_user.o
+test_cgrp2_array_pin-objs := test_cgrp2_array_pin.o
+test_cgrp2_attach-objs := test_cgrp2_attach.o
+test_cgrp2_attach2-objs := test_cgrp2_attach2.o $(CGROUP_HELPERS)
+test_cgrp2_sock-objs := test_cgrp2_sock.o
+test_cgrp2_sock2-objs := bpf_load.o test_cgrp2_sock2.o
+xdp1-objs := xdp1_user.o
 # reuse xdp1 source intentionally
-xdp2-objs := bpf_load.o $(LIBBPF) xdp1_user.o
-xdp_router_ipv4-objs := bpf_load.o $(LIBBPF) xdp_router_ipv4_user.o
-test_current_task_under_cgroup-objs := bpf_load.o $(LIBBPF) $(CGROUP_HELPERS) \
+xdp2-objs := xdp1_user.o
+xdp_router_ipv4-objs := bpf_load.o xdp_router_ipv4_user.o
+test_current_task_under_cgroup-objs := bpf_load.o $(CGROUP_HELPERS) \
                                       test_current_task_under_cgroup_user.o
-trace_event-objs := bpf_load.o $(LIBBPF) trace_event_user.o $(TRACE_HELPERS)
-sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o $(TRACE_HELPERS)
-tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o
-lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o
-xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o
-test_map_in_map-objs := bpf_load.o $(LIBBPF) test_map_in_map_user.o
-per_socket_stats_example-objs := $(LIBBPF) cookie_uid_helper_example.o
-xdp_redirect-objs := bpf_load.o $(LIBBPF) xdp_redirect_user.o
-xdp_redirect_map-objs := bpf_load.o $(LIBBPF) xdp_redirect_map_user.o
-xdp_redirect_cpu-objs := bpf_load.o $(LIBBPF) xdp_redirect_cpu_user.o
-xdp_monitor-objs := bpf_load.o $(LIBBPF) xdp_monitor_user.o
-xdp_rxq_info-objs := bpf_load.o $(LIBBPF) xdp_rxq_info_user.o
-syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o
-cpustat-objs := bpf_load.o $(LIBBPF) cpustat_user.o
-xdp_adjust_tail-objs := bpf_load.o $(LIBBPF) xdp_adjust_tail_user.o
-xdpsock-objs := bpf_load.o $(LIBBPF) xdpsock_user.o
+trace_event-objs := bpf_load.o trace_event_user.o $(TRACE_HELPERS)
+sampleip-objs := bpf_load.o sampleip_user.o $(TRACE_HELPERS)
+tc_l2_redirect-objs := bpf_load.o tc_l2_redirect_user.o
+lwt_len_hist-objs := bpf_load.o lwt_len_hist_user.o
+xdp_tx_iptunnel-objs := bpf_load.o xdp_tx_iptunnel_user.o
+test_map_in_map-objs := bpf_load.o test_map_in_map_user.o
+per_socket_stats_example-objs := cookie_uid_helper_example.o
+xdp_redirect-objs := bpf_load.o xdp_redirect_user.o
+xdp_redirect_map-objs := bpf_load.o xdp_redirect_map_user.o
+xdp_redirect_cpu-objs := bpf_load.o xdp_redirect_cpu_user.o
+xdp_monitor-objs := bpf_load.o xdp_monitor_user.o
+xdp_rxq_info-objs := xdp_rxq_info_user.o
+syscall_tp-objs := bpf_load.o syscall_tp_user.o
+cpustat-objs := bpf_load.o cpustat_user.o
+xdp_adjust_tail-objs := xdp_adjust_tail_user.o
+xdpsock-objs := bpf_load.o xdpsock_user.o
+xdp_fwd-objs := bpf_load.o xdp_fwd_user.o
 
 # Tell kbuild to always build the programs
 always := $(hostprogs-y)
@@ -154,6 +159,7 @@ always += syscall_tp_kern.o
 always += cpustat_kern.o
 always += xdp_adjust_tail_kern.o
 always += xdpsock_kern.o
+always += xdp_fwd_kern.o
 
 HOSTCFLAGS += -I$(objtree)/usr/include
 HOSTCFLAGS += -I$(srctree)/tools/lib/
@@ -162,45 +168,20 @@ HOSTCFLAGS += -I$(srctree)/tools/lib/ -I$(srctree)/tools/include
 HOSTCFLAGS += -I$(srctree)/tools/perf
 
 HOSTCFLAGS_bpf_load.o += -I$(objtree)/usr/include -Wno-unused-variable
-HOSTLOADLIBES_fds_example += -lelf
-HOSTLOADLIBES_sockex1 += -lelf
-HOSTLOADLIBES_sockex2 += -lelf
-HOSTLOADLIBES_sockex3 += -lelf
-HOSTLOADLIBES_tracex1 += -lelf
-HOSTLOADLIBES_tracex2 += -lelf
-HOSTLOADLIBES_tracex3 += -lelf
-HOSTLOADLIBES_tracex4 += -lelf -lrt
-HOSTLOADLIBES_tracex5 += -lelf
-HOSTLOADLIBES_tracex6 += -lelf
-HOSTLOADLIBES_tracex7 += -lelf
-HOSTLOADLIBES_test_cgrp2_sock2 += -lelf
-HOSTLOADLIBES_load_sock_ops += -lelf
-HOSTLOADLIBES_test_probe_write_user += -lelf
-HOSTLOADLIBES_trace_output += -lelf -lrt
-HOSTLOADLIBES_lathist += -lelf
-HOSTLOADLIBES_offwaketime += -lelf
-HOSTLOADLIBES_spintest += -lelf
-HOSTLOADLIBES_map_perf_test += -lelf -lrt
-HOSTLOADLIBES_test_overhead += -lelf -lrt
-HOSTLOADLIBES_xdp1 += -lelf
-HOSTLOADLIBES_xdp2 += -lelf
-HOSTLOADLIBES_xdp_router_ipv4 += -lelf
-HOSTLOADLIBES_test_current_task_under_cgroup += -lelf
-HOSTLOADLIBES_trace_event += -lelf
-HOSTLOADLIBES_sampleip += -lelf
-HOSTLOADLIBES_tc_l2_redirect += -l elf
-HOSTLOADLIBES_lwt_len_hist += -l elf
-HOSTLOADLIBES_xdp_tx_iptunnel += -lelf
-HOSTLOADLIBES_test_map_in_map += -lelf
-HOSTLOADLIBES_xdp_redirect += -lelf
-HOSTLOADLIBES_xdp_redirect_map += -lelf
-HOSTLOADLIBES_xdp_redirect_cpu += -lelf
-HOSTLOADLIBES_xdp_monitor += -lelf
-HOSTLOADLIBES_xdp_rxq_info += -lelf
-HOSTLOADLIBES_syscall_tp += -lelf
-HOSTLOADLIBES_cpustat += -lelf
-HOSTLOADLIBES_xdp_adjust_tail += -lelf
-HOSTLOADLIBES_xdpsock += -lelf -pthread
+HOSTCFLAGS_trace_helpers.o += -I$(srctree)/tools/lib/bpf/
+
+HOSTCFLAGS_trace_output_user.o += -I$(srctree)/tools/lib/bpf/
+HOSTCFLAGS_offwaketime_user.o += -I$(srctree)/tools/lib/bpf/
+HOSTCFLAGS_spintest_user.o += -I$(srctree)/tools/lib/bpf/
+HOSTCFLAGS_trace_event_user.o += -I$(srctree)/tools/lib/bpf/
+HOSTCFLAGS_sampleip_user.o += -I$(srctree)/tools/lib/bpf/
+
+HOST_LOADLIBES         += $(LIBBPF) -lelf
+HOSTLOADLIBES_tracex4          += -lrt
+HOSTLOADLIBES_trace_output     += -lrt
+HOSTLOADLIBES_map_perf_test    += -lrt
+HOSTLOADLIBES_test_overhead    += -lrt
+HOSTLOADLIBES_xdpsock          += -pthread
 
 # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
 #  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
@@ -214,15 +195,16 @@ CLANG_ARCH_ARGS = -target $(ARCH)
 endif
 
 # Trick to allow make to be run from this directory
-all: $(LIBBPF)
-       $(MAKE) -C ../../ $(CURDIR)/
+all:
+       $(MAKE) -C ../../ $(CURDIR)/ BPF_SAMPLES_PATH=$(CURDIR)
 
 clean:
        $(MAKE) -C ../../ M=$(CURDIR) clean
        @rm -f *~
 
 $(LIBBPF): FORCE
-       $(MAKE) -C $(dir $@) $(notdir $@)
+# Fix up variables inherited from Kbuild that tools/ build system won't like
+       $(MAKE) -C $(dir $@) RM='rm -rf' LDFLAGS= srctree=$(BPF_SAMPLES_PATH)/../../ O=
 
 $(obj)/syscall_nrs.s:  $(src)/syscall_nrs.c
        $(call if_changed_dep,cc_s_c)
@@ -253,7 +235,8 @@ verify_target_bpf: verify_cmds
                exit 2; \
        else true; fi
 
-$(src)/*.c: verify_target_bpf
+$(BPF_SAMPLES_PATH)/*.c: verify_target_bpf $(LIBBPF)
+$(src)/*.c: verify_target_bpf $(LIBBPF)
 
 $(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h
 
@@ -261,9 +244,10 @@ $(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h
 # But, there is no easy way to fix it, so just exclude it since it is
 # useless for BPF samples.
 $(obj)/%.o: $(src)/%.c
-       $(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) -I$(obj) \
+       @echo "  CLANG-bpf " $@
+       $(Q)$(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) -I$(obj) \
                -I$(srctree)/tools/testing/selftests/bpf/ \
-               -D__KERNEL__ -Wno-unused-value -Wno-pointer-sign \
+               -D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
                -D__TARGET_ARCH_$(ARCH) -Wno-compare-distinct-pointer-types \
                -Wno-gnu-variable-sized-type-not-at-end \
                -Wno-address-of-packed-member -Wno-tautological-compare \
diff --git a/samples/bpf/bpf_insn.h b/samples/bpf/bpf_insn.h
new file mode 100644 (file)
index 0000000..20dc5ce
--- /dev/null
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* eBPF instruction mini library */
+#ifndef __BPF_INSN_H
+#define __BPF_INSN_H
+
+struct bpf_insn;
+
+/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
+
+#define BPF_ALU64_REG(OP, DST, SRC)                            \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU64 | BPF_OP(OP) | BPF_X,        \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#define BPF_ALU32_REG(OP, DST, SRC)                            \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_OP(OP) | BPF_X,          \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
+
+#define BPF_ALU64_IMM(OP, DST, IMM)                            \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU64 | BPF_OP(OP) | BPF_K,        \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_ALU32_IMM(OP, DST, IMM)                            \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_OP(OP) | BPF_K,          \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Short form of mov, dst_reg = src_reg */
+
+#define BPF_MOV64_REG(DST, SRC)                                        \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_X,           \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#define BPF_MOV32_REG(DST, SRC)                                        \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_MOV | BPF_X,             \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+/* Short form of mov, dst_reg = imm32 */
+
+#define BPF_MOV64_IMM(DST, IMM)                                        \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_K,           \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_MOV32_IMM(DST, IMM)                                        \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ALU | BPF_MOV | BPF_K,             \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */
+#define BPF_LD_IMM64(DST, IMM)                                 \
+       BPF_LD_IMM64_RAW(DST, 0, IMM)
+
+#define BPF_LD_IMM64_RAW(DST, SRC, IMM)                                \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_LD | BPF_DW | BPF_IMM,             \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = (__u32) (IMM) }),                      \
+       ((struct bpf_insn) {                                    \
+               .code  = 0, /* zero is reserved opcode */       \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = ((__u64) (IMM)) >> 32 })
+
+#ifndef BPF_PSEUDO_MAP_FD
+# define BPF_PSEUDO_MAP_FD     1
+#endif
+
+/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
+#define BPF_LD_MAP_FD(DST, MAP_FD)                             \
+       BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
+
+
+/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */
+
+#define BPF_LD_ABS(SIZE, IMM)                                  \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS,     \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
+
+#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)                       \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,    \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
+
+#define BPF_STX_MEM(SIZE, DST, SRC, OFF)                       \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,    \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */
+
+#define BPF_STX_XADD(SIZE, DST, SRC, OFF)                      \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD,   \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
+
+#define BPF_ST_MEM(SIZE, DST, OFF, IMM)                                \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM,     \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
+
+#define BPF_JMP_REG(OP, DST, SRC, OFF)                         \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_JMP | BPF_OP(OP) | BPF_X,          \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
+
+#define BPF_JMP_IMM(OP, DST, IMM, OFF)                         \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_JMP | BPF_OP(OP) | BPF_K,          \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Raw code statement block */
+
+#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM)                 \
+       ((struct bpf_insn) {                                    \
+               .code  = CODE,                                  \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Program exit */
+
+#define BPF_EXIT_INSN()                                                \
+       ((struct bpf_insn) {                                    \
+               .code  = BPF_JMP | BPF_EXIT,                    \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#endif
index da9bccf..89161c9 100644 (file)
@@ -24,7 +24,7 @@
 #include <poll.h>
 #include <ctype.h>
 #include <assert.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include "perf-sys.h"
 
@@ -420,7 +420,7 @@ static int load_elf_maps_section(struct bpf_map_data *maps, int maps_shndx,
 
        /* Keeping compatible with ELF maps section changes
         * ------------------------------------------------
-        * The program size of struct bpf_map_def is known by loader
+        * The program size of struct bpf_load_map_def is known by loader
         * code, but struct stored in ELF file can be different.
         *
         * Unfortunately sym[i].st_size is zero.  To calculate the
@@ -429,7 +429,7 @@ static int load_elf_maps_section(struct bpf_map_data *maps, int maps_shndx,
         * symbols.
         */
        map_sz_elf = data_maps->d_size / nr_maps;
-       map_sz_copy = sizeof(struct bpf_map_def);
+       map_sz_copy = sizeof(struct bpf_load_map_def);
        if (map_sz_elf < map_sz_copy) {
                /*
                 * Backward compat, loading older ELF file with
@@ -448,8 +448,8 @@ static int load_elf_maps_section(struct bpf_map_data *maps, int maps_shndx,
 
        /* Memcpy relevant part of ELF maps data to loader maps */
        for (i = 0; i < nr_maps; i++) {
+               struct bpf_load_map_def *def;
                unsigned char *addr, *end;
-               struct bpf_map_def *def;
                const char *map_name;
                size_t offset;
 
@@ -464,9 +464,9 @@ static int load_elf_maps_section(struct bpf_map_data *maps, int maps_shndx,
 
                /* Symbol value is offset into ELF maps section data area */
                offset = sym[i].st_value;
-               def = (struct bpf_map_def *)(data_maps->d_buf + offset);
+               def = (struct bpf_load_map_def *)(data_maps->d_buf + offset);
                maps[i].elf_offset = offset;
-               memset(&maps[i].def, 0, sizeof(struct bpf_map_def));
+               memset(&maps[i].def, 0, sizeof(struct bpf_load_map_def));
                memcpy(&maps[i].def, def, map_sz_copy);
 
                /* Verify no newer features were requested */
index 2c3d0b4..814894a 100644 (file)
@@ -2,12 +2,12 @@
 #ifndef __BPF_LOAD_H
 #define __BPF_LOAD_H
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 
 #define MAX_MAPS 32
 #define MAX_PROGS 32
 
-struct bpf_map_def {
+struct bpf_load_map_def {
        unsigned int type;
        unsigned int key_size;
        unsigned int value_size;
@@ -21,7 +21,7 @@ struct bpf_map_data {
        int fd;
        char *name;
        size_t elf_offset;
-       struct bpf_map_def def;
+       struct bpf_load_map_def def;
 };
 
 typedef void (*fixup_map_cb)(struct bpf_map_data *map, int idx);
index 8eca27e..deb0e3e 100644 (file)
@@ -51,7 +51,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <bpf/bpf.h>
-#include "libbpf.h"
+#include "bpf_insn.h"
 
 #define PORT 8888
 
index 2b4cd1a..869a994 100644 (file)
@@ -17,7 +17,7 @@
 #include <sys/resource.h>
 #include <sys/wait.h>
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 
 #define MAX_CPU                        8
index e29bd52..9854854 100644 (file)
 #include <sys/types.h>
 #include <sys/socket.h>
 
+#include <bpf/bpf.h>
+
+#include "bpf_insn.h"
 #include "bpf_load.h"
-#include "libbpf.h"
 #include "sock_example.h"
 
 #define BPF_F_PIN      (1 << 0)
index 6477bad..c8e88cc 100644 (file)
@@ -10,7 +10,7 @@
 #include <stdlib.h>
 #include <signal.h>
 #include <linux/bpf.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 
 #define MAX_ENTRIES    20
diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h
deleted file mode 100644 (file)
index 18bfee5..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* eBPF mini library */
-#ifndef __LIBBPF_H
-#define __LIBBPF_H
-
-#include <bpf/bpf.h>
-
-struct bpf_insn;
-
-/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
-
-#define BPF_ALU64_REG(OP, DST, SRC)                            \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_ALU64 | BPF_OP(OP) | BPF_X,        \
-               .dst_reg = DST,                                 \
-               .src_reg = SRC,                                 \
-               .off   = 0,                                     \
-               .imm   = 0 })
-
-#define BPF_ALU32_REG(OP, DST, SRC)                            \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_ALU | BPF_OP(OP) | BPF_X,          \
-               .dst_reg = DST,                                 \
-               .src_reg = SRC,                                 \
-               .off   = 0,                                     \
-               .imm   = 0 })
-
-/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
-
-#define BPF_ALU64_IMM(OP, DST, IMM)                            \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_ALU64 | BPF_OP(OP) | BPF_K,        \
-               .dst_reg = DST,                                 \
-               .src_reg = 0,                                   \
-               .off   = 0,                                     \
-               .imm   = IMM })
-
-#define BPF_ALU32_IMM(OP, DST, IMM)                            \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_ALU | BPF_OP(OP) | BPF_K,          \
-               .dst_reg = DST,                                 \
-               .src_reg = 0,                                   \
-               .off   = 0,                                     \
-               .imm   = IMM })
-
-/* Short form of mov, dst_reg = src_reg */
-
-#define BPF_MOV64_REG(DST, SRC)                                        \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_ALU64 | BPF_MOV | BPF_X,           \
-               .dst_reg = DST,                                 \
-               .src_reg = SRC,                                 \
-               .off   = 0,                                     \
-               .imm   = 0 })
-
-#define BPF_MOV32_REG(DST, SRC)                                        \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_ALU | BPF_MOV | BPF_X,             \
-               .dst_reg = DST,                                 \
-               .src_reg = SRC,                                 \
-               .off   = 0,                                     \
-               .imm   = 0 })
-
-/* Short form of mov, dst_reg = imm32 */
-
-#define BPF_MOV64_IMM(DST, IMM)                                        \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_ALU64 | BPF_MOV | BPF_K,           \
-               .dst_reg = DST,                                 \
-               .src_reg = 0,                                   \
-               .off   = 0,                                     \
-               .imm   = IMM })
-
-#define BPF_MOV32_IMM(DST, IMM)                                        \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_ALU | BPF_MOV | BPF_K,             \
-               .dst_reg = DST,                                 \
-               .src_reg = 0,                                   \
-               .off   = 0,                                     \
-               .imm   = IMM })
-
-/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */
-#define BPF_LD_IMM64(DST, IMM)                                 \
-       BPF_LD_IMM64_RAW(DST, 0, IMM)
-
-#define BPF_LD_IMM64_RAW(DST, SRC, IMM)                                \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_LD | BPF_DW | BPF_IMM,             \
-               .dst_reg = DST,                                 \
-               .src_reg = SRC,                                 \
-               .off   = 0,                                     \
-               .imm   = (__u32) (IMM) }),                      \
-       ((struct bpf_insn) {                                    \
-               .code  = 0, /* zero is reserved opcode */       \
-               .dst_reg = 0,                                   \
-               .src_reg = 0,                                   \
-               .off   = 0,                                     \
-               .imm   = ((__u64) (IMM)) >> 32 })
-
-#ifndef BPF_PSEUDO_MAP_FD
-# define BPF_PSEUDO_MAP_FD     1
-#endif
-
-/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
-#define BPF_LD_MAP_FD(DST, MAP_FD)                             \
-       BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
-
-
-/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */
-
-#define BPF_LD_ABS(SIZE, IMM)                                  \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS,     \
-               .dst_reg = 0,                                   \
-               .src_reg = 0,                                   \
-               .off   = 0,                                     \
-               .imm   = IMM })
-
-/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
-
-#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)                       \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,    \
-               .dst_reg = DST,                                 \
-               .src_reg = SRC,                                 \
-               .off   = OFF,                                   \
-               .imm   = 0 })
-
-/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
-
-#define BPF_STX_MEM(SIZE, DST, SRC, OFF)                       \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,    \
-               .dst_reg = DST,                                 \
-               .src_reg = SRC,                                 \
-               .off   = OFF,                                   \
-               .imm   = 0 })
-
-/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */
-
-#define BPF_STX_XADD(SIZE, DST, SRC, OFF)                      \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD,   \
-               .dst_reg = DST,                                 \
-               .src_reg = SRC,                                 \
-               .off   = OFF,                                   \
-               .imm   = 0 })
-
-/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
-
-#define BPF_ST_MEM(SIZE, DST, OFF, IMM)                                \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM,     \
-               .dst_reg = DST,                                 \
-               .src_reg = 0,                                   \
-               .off   = OFF,                                   \
-               .imm   = IMM })
-
-/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
-
-#define BPF_JMP_REG(OP, DST, SRC, OFF)                         \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_JMP | BPF_OP(OP) | BPF_X,          \
-               .dst_reg = DST,                                 \
-               .src_reg = SRC,                                 \
-               .off   = OFF,                                   \
-               .imm   = 0 })
-
-/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
-
-#define BPF_JMP_IMM(OP, DST, IMM, OFF)                         \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_JMP | BPF_OP(OP) | BPF_K,          \
-               .dst_reg = DST,                                 \
-               .src_reg = 0,                                   \
-               .off   = OFF,                                   \
-               .imm   = IMM })
-
-/* Raw code statement block */
-
-#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM)                 \
-       ((struct bpf_insn) {                                    \
-               .code  = CODE,                                  \
-               .dst_reg = DST,                                 \
-               .src_reg = SRC,                                 \
-               .off   = OFF,                                   \
-               .imm   = IMM })
-
-/* Program exit */
-
-#define BPF_EXIT_INSN()                                                \
-       ((struct bpf_insn) {                                    \
-               .code  = BPF_JMP | BPF_EXIT,                    \
-               .dst_reg = 0,                                   \
-               .src_reg = 0,                                   \
-               .off   = 0,                                     \
-               .imm   = 0 })
-
-#endif
index e5da6cf..8ecb41e 100644 (file)
@@ -8,7 +8,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <linux/bpf.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include <unistd.h>
 #include <errno.h>
index 7fcb94c..587b68b 100644 (file)
@@ -9,7 +9,7 @@
 #include <errno.h>
 #include <arpa/inet.h>
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_util.h"
 
 #define MAX_INDEX 64
index 519d9af..38b7b1a 100644 (file)
@@ -21,7 +21,7 @@
 #include <arpa/inet.h>
 #include <errno.h>
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 
 #define TEST_BIT(t) (1U << (t))
index 33a6375..60ec467 100644 (file)
@@ -26,7 +26,8 @@
 #include <linux/if_ether.h>
 #include <linux/ip.h>
 #include <stddef.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
+#include "bpf_insn.h"
 #include "sock_example.h"
 
 char bpf_log_buf[BPF_LOG_BUF_SIZE];
index 772d5da..a27d757 100644 (file)
@@ -9,7 +9,6 @@
 #include <net/if.h>
 #include <linux/if_packet.h>
 #include <arpa/inet.h>
-#include "libbpf.h"
 
 static inline int open_raw_sock(const char *name)
 {
index 2be935c..93ec01c 100644 (file)
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <assert.h>
 #include <linux/bpf.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include "sock_example.h"
 #include <unistd.h>
index 44fe080..1d5c6e9 100644 (file)
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <assert.h>
 #include <linux/bpf.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include "sock_example.h"
 #include <unistd.h>
index 495ee02..5ba3ae9 100644 (file)
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <assert.h>
 #include <linux/bpf.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include "sock_example.h"
 #include <unistd.h>
index 9169d32..1a1d005 100644 (file)
@@ -16,7 +16,7 @@
 #include <assert.h>
 #include <stdbool.h>
 #include <sys/resource.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 
 /* This program verifies bpf attachment to tracepoint sys_enter_* and sys_exit_*.
index 28995a7..7ec45c3 100644 (file)
@@ -13,7 +13,7 @@
 #include <string.h>
 #include <errno.h>
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 
 static void usage(void)
 {
index 8a1b8b5..2421842 100644 (file)
@@ -14,7 +14,7 @@
 #include <errno.h>
 #include <fcntl.h>
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 
 static void usage(void)
 {
index 4bfcaf9..20fbd12 100644 (file)
@@ -28,8 +28,9 @@
 #include <fcntl.h>
 
 #include <linux/bpf.h>
+#include <bpf/bpf.h>
 
-#include "libbpf.h"
+#include "bpf_insn.h"
 
 enum {
        MAP_KEY_PACKETS,
index 1af412e..b453e6a 100644 (file)
@@ -24,8 +24,9 @@
 #include <unistd.h>
 
 #include <linux/bpf.h>
+#include <bpf/bpf.h>
 
-#include "libbpf.h"
+#include "bpf_insn.h"
 #include "cgroup_helpers.h"
 
 #define FOO            "/foo"
index e79594d..b0811da 100644 (file)
@@ -21,8 +21,9 @@
 #include <net/if.h>
 #include <inttypes.h>
 #include <linux/bpf.h>
+#include <bpf/bpf.h>
 
-#include "libbpf.h"
+#include "bpf_insn.h"
 
 char bpf_log_buf[BPF_LOG_BUF_SIZE];
 
index e53f1f6..3b5be23 100644 (file)
@@ -19,8 +19,9 @@
 #include <fcntl.h>
 #include <net/if.h>
 #include <linux/bpf.h>
+#include <bpf/bpf.h>
 
-#include "libbpf.h"
+#include "bpf_insn.h"
 #include "bpf_load.h"
 
 static int usage(const char *argv0)
index 65b5fb5..4be4874 100644 (file)
@@ -9,7 +9,7 @@
 #include <stdio.h>
 #include <linux/bpf.h>
 #include <unistd.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include <linux/bpf.h>
 #include "cgroup_helpers.h"
index 73c3571..eec3e25 100644 (file)
@@ -21,7 +21,7 @@
 #include <stdlib.h>
 #include <time.h>
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_util.h"
 
 #define min(a, b) ((a) < (b) ? (a) : (b))
index 1aca185..e308858 100644 (file)
@@ -13,7 +13,7 @@
 #include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 
 #define PORT_A         (map_fd[0])
index e1d35e0..6caf47a 100644 (file)
@@ -19,7 +19,7 @@
 #include <string.h>
 #include <time.h>
 #include <sys/resource.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 
 #define MAX_CNT 1000000
index bf8e3a9..045eb5e 100644 (file)
@@ -3,7 +3,7 @@
 #include <assert.h>
 #include <linux/bpf.h>
 #include <unistd.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include <sys/socket.h>
 #include <string.h>
index 5e78c2e..4837d73 100644 (file)
@@ -18,7 +18,7 @@
 #include <sys/mman.h>
 #include <time.h>
 #include <signal.h>
-#include "libbpf.h"
+#include <libbpf.h>
 #include "bpf_load.h"
 #include "perf-sys.h"
 #include "trace_helpers.h"
@@ -48,7 +48,7 @@ static int print_bpf_output(void *data, int size)
        if (e->cookie != 0x12345678) {
                printf("BUG pid %llx cookie %llx sized %d\n",
                       e->pid, e->cookie, size);
-               return PERF_EVENT_ERROR;
+               return LIBBPF_PERF_EVENT_ERROR;
        }
 
        cnt++;
@@ -56,10 +56,10 @@ static int print_bpf_output(void *data, int size)
        if (cnt == MAX_CNT) {
                printf("recv %lld events per sec\n",
                       MAX_CNT * 1000000000ll / (time_get_ns() - start_time));
-               return PERF_EVENT_DONE;
+               return LIBBPF_PERF_EVENT_DONE;
        }
 
-       return PERF_EVENT_CONT;
+       return LIBBPF_PERF_EVENT_CONT;
 }
 
 static void test_bpf_perf_event(void)
index 3dcb475..af8c206 100644 (file)
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <linux/bpf.h>
 #include <unistd.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 
 int main(int ac, char **argv)
index efb5e61..1a81e6a 100644 (file)
@@ -7,7 +7,7 @@
 #include <string.h>
 #include <sys/resource.h>
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include "bpf_util.h"
 
index fe37223..6c6b10f 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/bpf.h>
 #include <sys/resource.h>
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include "bpf_util.h"
 
index 22c644f..14625c8 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/bpf.h>
 #include <sys/resource.h>
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 
 struct pair {
index 4e2774b..c4ab91c 100644 (file)
@@ -5,7 +5,7 @@
 #include <linux/filter.h>
 #include <linux/seccomp.h>
 #include <sys/prctl.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include <sys/resource.h>
 
index 89ab8d4..4bb3c83 100644 (file)
@@ -16,7 +16,7 @@
 #include <unistd.h>
 
 #include "bpf_load.h"
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "perf-sys.h"
 
 #define SAMPLE_PERIOD  0x7fffffffffffffffULL
index 8a52ac4..ea6dae7 100644 (file)
@@ -3,7 +3,7 @@
 #include <stdio.h>
 #include <linux/bpf.h>
 #include <unistd.h>
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 
 int main(int argc, char **argv)
index b901ee2..b02c531 100644 (file)
@@ -16,9 +16,9 @@
 #include <libgen.h>
 #include <sys/resource.h>
 
-#include "bpf_load.h"
 #include "bpf_util.h"
-#include "libbpf.h"
+#include "bpf/bpf.h"
+#include "bpf/libbpf.h"
 
 static int ifindex;
 static __u32 xdp_flags;
@@ -31,7 +31,7 @@ static void int_exit(int sig)
 
 /* simple per-protocol drop counter
  */
-static void poll_stats(int interval)
+static void poll_stats(int map_fd, int interval)
 {
        unsigned int nr_cpus = bpf_num_possible_cpus();
        const unsigned int nr_keys = 256;
@@ -47,7 +47,7 @@ static void poll_stats(int interval)
                for (key = 0; key < nr_keys; key++) {
                        __u64 sum = 0;
 
-                       assert(bpf_map_lookup_elem(map_fd[0], &key, values) == 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)
@@ -71,9 +71,14 @@ static void usage(const char *prog)
 int main(int argc, char **argv)
 {
        struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
+       struct bpf_prog_load_attr prog_load_attr = {
+               .prog_type      = BPF_PROG_TYPE_XDP,
+       };
        const char *optstr = "SN";
+       int prog_fd, map_fd, opt;
+       struct bpf_object *obj;
+       struct bpf_map *map;
        char filename[256];
-       int opt;
 
        while ((opt = getopt(argc, argv, optstr)) != -1) {
                switch (opt) {
@@ -102,13 +107,19 @@ int main(int argc, char **argv)
        ifindex = strtoul(argv[optind], NULL, 0);
 
        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+       prog_load_attr.file = filename;
 
-       if (load_bpf_file(filename)) {
-               printf("%s", bpf_log_buf);
+       if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
+               return 1;
+
+       map = bpf_map__next(NULL, obj);
+       if (!map) {
+               printf("finding a map in obj file failed\n");
                return 1;
        }
+       map_fd = bpf_map__fd(map);
 
-       if (!prog_fd[0]) {
+       if (!prog_fd) {
                printf("load_bpf_file: %s\n", strerror(errno));
                return 1;
        }
@@ -116,12 +127,12 @@ int main(int argc, char **argv)
        signal(SIGINT, int_exit);
        signal(SIGTERM, int_exit);
 
-       if (bpf_set_link_xdp_fd(ifindex, prog_fd[0], xdp_flags) < 0) {
+       if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
                printf("link set xdp fd failed\n");
                return 1;
        }
 
-       poll_stats(2);
+       poll_stats(map_fd, 2);
 
        return 0;
 }
index f621a54..3042ce3 100644 (file)
@@ -18,9 +18,8 @@
 #include <netinet/ether.h>
 #include <unistd.h>
 #include <time.h>
-#include "bpf_load.h"
-#include "libbpf.h"
-#include "bpf_util.h"
+#include "bpf/bpf.h"
+#include "bpf/libbpf.h"
 
 #define STATS_INTERVAL_S 2U
 
@@ -36,7 +35,7 @@ static void int_exit(int sig)
 
 /* simple "icmp packet too big sent" counter
  */
-static void poll_stats(unsigned int kill_after_s)
+static void poll_stats(unsigned int map_fd, unsigned int kill_after_s)
 {
        time_t started_at = time(NULL);
        __u64 value = 0;
@@ -46,7 +45,7 @@ static void poll_stats(unsigned int kill_after_s)
        while (!kill_after_s || time(NULL) - started_at <= kill_after_s) {
                sleep(STATS_INTERVAL_S);
 
-               assert(bpf_map_lookup_elem(map_fd[0], &key, &value) == 0);
+               assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
 
                printf("icmp \"packet too big\" sent: %10llu pkts\n", value);
        }
@@ -66,14 +65,17 @@ static void usage(const char *cmd)
 
 int main(int argc, char **argv)
 {
+       struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
+       struct bpf_prog_load_attr prog_load_attr = {
+               .prog_type      = BPF_PROG_TYPE_XDP,
+       };
        unsigned char opt_flags[256] = {};
        unsigned int kill_after_s = 0;
        const char *optstr = "i:T:SNh";
-       struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
+       int i, prog_fd, map_fd, opt;
+       struct bpf_object *obj;
+       struct bpf_map *map;
        char filename[256];
-       int opt;
-       int i;
-
 
        for (i = 0; i < strlen(optstr); i++)
                if (optstr[i] != 'h' && 'a' <= optstr[i] && optstr[i] <= 'z')
@@ -115,13 +117,19 @@ int main(int argc, char **argv)
        }
 
        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+       prog_load_attr.file = filename;
+
+       if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
+               return 1;
 
-       if (load_bpf_file(filename)) {
-               printf("%s", bpf_log_buf);
+       map = bpf_map__next(NULL, obj);
+       if (!map) {
+               printf("finding a map in obj file failed\n");
                return 1;
        }
+       map_fd = bpf_map__fd(map);
 
-       if (!prog_fd[0]) {
+       if (!prog_fd) {
                printf("load_bpf_file: %s\n", strerror(errno));
                return 1;
        }
@@ -129,12 +137,12 @@ int main(int argc, char **argv)
        signal(SIGINT, int_exit);
        signal(SIGTERM, int_exit);
 
-       if (bpf_set_link_xdp_fd(ifindex, prog_fd[0], xdp_flags) < 0) {
+       if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
                printf("link set xdp fd failed\n");
                return 1;
        }
 
-       poll_stats(kill_after_s);
+       poll_stats(map_fd, kill_after_s);
 
        bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
 
diff --git a/samples/bpf/xdp_fwd_kern.c b/samples/bpf/xdp_fwd_kern.c
new file mode 100644 (file)
index 0000000..4a6be0f
--- /dev/null
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2017-18 David Ahern <dsahern@gmail.com>
+ *
+ * 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.
+ */
+#define KBUILD_MODNAME "foo"
+#include <uapi/linux/bpf.h>
+#include <linux/in.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+
+#include "bpf_helpers.h"
+
+#define IPV6_FLOWINFO_MASK              cpu_to_be32(0x0FFFFFFF)
+
+struct bpf_map_def SEC("maps") tx_port = {
+       .type = BPF_MAP_TYPE_DEVMAP,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 64,
+};
+
+/* from include/net/ip.h */
+static __always_inline int ip_decrease_ttl(struct iphdr *iph)
+{
+       u32 check = (__force u32)iph->check;
+
+       check += (__force u32)htons(0x0100);
+       iph->check = (__force __sum16)(check + (check >= 0xFFFF));
+       return --iph->ttl;
+}
+
+static __always_inline int xdp_fwd_flags(struct xdp_md *ctx, u32 flags)
+{
+       void *data_end = (void *)(long)ctx->data_end;
+       void *data = (void *)(long)ctx->data;
+       struct bpf_fib_lookup fib_params;
+       struct ethhdr *eth = data;
+       struct ipv6hdr *ip6h;
+       struct iphdr *iph;
+       int out_index;
+       u16 h_proto;
+       u64 nh_off;
+
+       nh_off = sizeof(*eth);
+       if (data + nh_off > data_end)
+               return XDP_DROP;
+
+       __builtin_memset(&fib_params, 0, sizeof(fib_params));
+
+       h_proto = eth->h_proto;
+       if (h_proto == htons(ETH_P_IP)) {
+               iph = data + nh_off;
+
+               if (iph + 1 > data_end)
+                       return XDP_DROP;
+
+               if (iph->ttl <= 1)
+                       return XDP_PASS;
+
+               fib_params.family       = AF_INET;
+               fib_params.tos          = iph->tos;
+               fib_params.l4_protocol  = iph->protocol;
+               fib_params.sport        = 0;
+               fib_params.dport        = 0;
+               fib_params.tot_len      = ntohs(iph->tot_len);
+               fib_params.ipv4_src     = iph->saddr;
+               fib_params.ipv4_dst     = iph->daddr;
+       } else if (h_proto == htons(ETH_P_IPV6)) {
+               struct in6_addr *src = (struct in6_addr *) fib_params.ipv6_src;
+               struct in6_addr *dst = (struct in6_addr *) fib_params.ipv6_dst;
+
+               ip6h = data + nh_off;
+               if (ip6h + 1 > data_end)
+                       return XDP_DROP;
+
+               if (ip6h->hop_limit <= 1)
+                       return XDP_PASS;
+
+               fib_params.family       = AF_INET6;
+               fib_params.flowlabel    = *(__be32 *)ip6h & IPV6_FLOWINFO_MASK;
+               fib_params.l4_protocol  = ip6h->nexthdr;
+               fib_params.sport        = 0;
+               fib_params.dport        = 0;
+               fib_params.tot_len      = ntohs(ip6h->payload_len);
+               *src                    = ip6h->saddr;
+               *dst                    = ip6h->daddr;
+       } else {
+               return XDP_PASS;
+       }
+
+       fib_params.ifindex = ctx->ingress_ifindex;
+
+       out_index = bpf_fib_lookup(ctx, &fib_params, sizeof(fib_params), flags);
+
+       /* verify egress index has xdp support
+        * TO-DO bpf_map_lookup_elem(&tx_port, &key) fails with
+        *       cannot pass map_type 14 into func bpf_map_lookup_elem#1:
+        * NOTE: without verification that egress index supports XDP
+        *       forwarding packets are dropped.
+        */
+       if (out_index > 0) {
+               if (h_proto == htons(ETH_P_IP))
+                       ip_decrease_ttl(iph);
+               else if (h_proto == htons(ETH_P_IPV6))
+                       ip6h->hop_limit--;
+
+               memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
+               memcpy(eth->h_source, fib_params.smac, ETH_ALEN);
+               return bpf_redirect_map(&tx_port, out_index, 0);
+       }
+
+       return XDP_PASS;
+}
+
+SEC("xdp_fwd")
+int xdp_fwd_prog(struct xdp_md *ctx)
+{
+       return xdp_fwd_flags(ctx, 0);
+}
+
+SEC("xdp_fwd_direct")
+int xdp_fwd_direct_prog(struct xdp_md *ctx)
+{
+       return xdp_fwd_flags(ctx, BPF_FIB_LOOKUP_DIRECT);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/samples/bpf/xdp_fwd_user.c b/samples/bpf/xdp_fwd_user.c
new file mode 100644 (file)
index 0000000..a87a204
--- /dev/null
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2017-18 David Ahern <dsahern@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/bpf.h>
+#include <linux/if_link.h>
+#include <linux/limits.h>
+#include <net/if.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgen.h>
+
+#include "bpf_load.h"
+#include "bpf_util.h"
+#include <bpf/bpf.h>
+
+
+static int do_attach(int idx, int fd, const char *name)
+{
+       int err;
+
+       err = bpf_set_link_xdp_fd(idx, fd, 0);
+       if (err < 0)
+               printf("ERROR: failed to attach program to %s\n", name);
+
+       return err;
+}
+
+static int do_detach(int idx, const char *name)
+{
+       int err;
+
+       err = bpf_set_link_xdp_fd(idx, -1, 0);
+       if (err < 0)
+               printf("ERROR: failed to detach program from %s\n", name);
+
+       return err;
+}
+
+static void usage(const char *prog)
+{
+       fprintf(stderr,
+               "usage: %s [OPTS] interface-list\n"
+               "\nOPTS:\n"
+               "    -d    detach program\n"
+               "    -D    direct table lookups (skip fib rules)\n",
+               prog);
+}
+
+int main(int argc, char **argv)
+{
+       char filename[PATH_MAX];
+       int opt, i, idx, err;
+       int prog_id = 0;
+       int attach = 1;
+       int ret = 0;
+
+       while ((opt = getopt(argc, argv, ":dD")) != -1) {
+               switch (opt) {
+               case 'd':
+                       attach = 0;
+                       break;
+               case 'D':
+                       prog_id = 1;
+                       break;
+               default:
+                       usage(basename(argv[0]));
+                       return 1;
+               }
+       }
+
+       if (optind == argc) {
+               usage(basename(argv[0]));
+               return 1;
+       }
+
+       if (attach) {
+               snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+
+               if (access(filename, O_RDONLY) < 0) {
+                       printf("error accessing file %s: %s\n",
+                               filename, strerror(errno));
+                       return 1;
+               }
+
+               if (load_bpf_file(filename)) {
+                       printf("%s", bpf_log_buf);
+                       return 1;
+               }
+
+               if (!prog_fd[prog_id]) {
+                       printf("load_bpf_file: %s\n", strerror(errno));
+                       return 1;
+               }
+       }
+       if (attach) {
+               for (i = 1; i < 64; ++i)
+                       bpf_map_update_elem(map_fd[0], &i, &i, 0);
+       }
+
+       for (i = optind; i < argc; ++i) {
+               idx = if_nametoindex(argv[i]);
+               if (!idx)
+                       idx = strtoul(argv[i], NULL, 0);
+
+               if (!idx) {
+                       fprintf(stderr, "Invalid arg\n");
+                       return 1;
+               }
+               if (!attach) {
+                       err = do_detach(idx, argv[i]);
+                       if (err)
+                               ret = err;
+               } else {
+                       err = do_attach(idx, prog_fd[prog_id], argv[i]);
+                       if (err)
+                               ret = err;
+               }
+       }
+
+       return ret;
+}
index 894bc64..bf09b51 100644 (file)
@@ -26,7 +26,7 @@ static const char *__doc_err_only__=
 #include <net/if.h>
 #include <time.h>
 
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 #include "bpf_util.h"
 
@@ -58,7 +58,7 @@ static void usage(char *argv[])
                        printf(" flag (internal value:%d)",
                               *long_options[i].flag);
                else
-                       printf("(internal short-option: -%c)",
+                       printf("short-option: -%c",
                               long_options[i].val);
                printf("\n");
        }
@@ -594,7 +594,7 @@ int main(int argc, char **argv)
        snprintf(bpf_obj_file, sizeof(bpf_obj_file), "%s_kern.o", argv[0]);
 
        /* Parse commands line args */
-       while ((opt = getopt_long(argc, argv, "h",
+       while ((opt = getopt_long(argc, argv, "hDSs:",
                                  long_options, &longindex)) != -1) {
                switch (opt) {
                case 'D':
index 23744a8..f6efaef 100644 (file)
@@ -28,7 +28,7 @@ static const char *__doc__ =
  * use bpf/libbpf.h), but cannot as (currently) needed for XDP
  * attaching to a device via bpf_set_link_xdp_fd()
  */
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_load.h"
 
 #include "bpf_util.h"
index 7eae07d..4445e76 100644 (file)
@@ -24,7 +24,7 @@
 
 #include "bpf_load.h"
 #include "bpf_util.h"
-#include "libbpf.h"
+#include <bpf/bpf.h>
 
 static int ifindex_in;
 static int ifindex_out;
index b701b5c..81a69e3 100644 (file)
@@ -24,7 +24,7 @@
 
 #include "bpf_load.h"
 #include "bpf_util.h"
-#include "libbpf.h"
+#include <bpf/bpf.h>
 
 static int ifindex_in;
 static int ifindex_out;
index 6296741..b2b4dfa 100644 (file)
@@ -16,7 +16,7 @@
 #include <sys/socket.h>
 #include <unistd.h>
 #include "bpf_load.h"
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <poll.h>
index 478d954..e4e9ba5 100644 (file)
@@ -22,8 +22,8 @@ static const char *__doc__ = " XDP RX-queue info extract example\n\n"
 #include <arpa/inet.h>
 #include <linux/if_link.h>
 
-#include "libbpf.h"
-#include "bpf_load.h"
+#include "bpf/bpf.h"
+#include "bpf/libbpf.h"
 #include "bpf_util.h"
 
 static int ifindex = -1;
@@ -32,6 +32,9 @@ static char *ifname;
 
 static __u32 xdp_flags;
 
+static struct bpf_map *stats_global_map;
+static struct bpf_map *rx_queue_index_map;
+
 /* Exit return codes */
 #define EXIT_OK                0
 #define EXIT_FAIL              1
@@ -174,7 +177,7 @@ static struct datarec *alloc_record_per_cpu(void)
 
 static struct record *alloc_record_per_rxq(void)
 {
-       unsigned int nr_rxqs = map_data[2].def.max_entries;
+       unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries;
        struct record *array;
        size_t size;
 
@@ -190,7 +193,7 @@ static struct record *alloc_record_per_rxq(void)
 
 static struct stats_record *alloc_stats_record(void)
 {
-       unsigned int nr_rxqs = map_data[2].def.max_entries;
+       unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries;
        struct stats_record *rec;
        int i;
 
@@ -210,7 +213,7 @@ static struct stats_record *alloc_stats_record(void)
 
 static void free_stats_record(struct stats_record *r)
 {
-       unsigned int nr_rxqs = map_data[2].def.max_entries;
+       unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries;
        int i;
 
        for (i = 0; i < nr_rxqs; i++)
@@ -254,11 +257,11 @@ static void stats_collect(struct stats_record *rec)
 {
        int fd, i, max_rxqs;
 
-       fd = map_data[1].fd; /* map: stats_global_map */
+       fd = bpf_map__fd(stats_global_map);
        map_collect_percpu(fd, 0, &rec->stats);
 
-       fd = map_data[2].fd; /* map: rx_queue_index_map */
-       max_rxqs = map_data[2].def.max_entries;
+       fd = bpf_map__fd(rx_queue_index_map);
+       max_rxqs = bpf_map__def(rx_queue_index_map)->max_entries;
        for (i = 0; i < max_rxqs; i++)
                map_collect_percpu(fd, i, &rec->rxq[i]);
 }
@@ -304,8 +307,8 @@ static void stats_print(struct stats_record *stats_rec,
                        struct stats_record *stats_prev,
                        int action)
 {
+       unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries;
        unsigned int nr_cpus = bpf_num_possible_cpus();
-       unsigned int nr_rxqs = map_data[2].def.max_entries;
        double pps = 0, err = 0;
        struct record *rec, *prev;
        double t;
@@ -419,31 +422,44 @@ static void stats_poll(int interval, int action)
 int main(int argc, char **argv)
 {
        struct rlimit r = {10 * 1024 * 1024, RLIM_INFINITY};
+       struct bpf_prog_load_attr prog_load_attr = {
+               .prog_type      = BPF_PROG_TYPE_XDP,
+       };
+       int prog_fd, map_fd, opt, err;
        bool use_separators = true;
        struct config cfg = { 0 };
+       struct bpf_object *obj;
+       struct bpf_map *map;
        char filename[256];
        int longindex = 0;
        int interval = 2;
        __u32 key = 0;
-       int opt, err;
 
        char action_str_buf[XDP_ACTION_MAX_STRLEN + 1 /* for \0 */] = { 0 };
        int action = XDP_PASS; /* Default action */
        char *action_str = NULL;
 
        snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+       prog_load_attr.file = filename;
 
        if (setrlimit(RLIMIT_MEMLOCK, &r)) {
                perror("setrlimit(RLIMIT_MEMLOCK)");
                return 1;
        }
 
-       if (load_bpf_file(filename)) {
-               fprintf(stderr, "ERR in load_bpf_file(): %s", bpf_log_buf);
+       if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
+               return EXIT_FAIL;
+
+       map = bpf_map__next(NULL, obj);
+       stats_global_map = bpf_map__next(map, obj);
+       rx_queue_index_map = bpf_map__next(stats_global_map, obj);
+       if (!map || !stats_global_map || !rx_queue_index_map) {
+               printf("finding a map in obj file failed\n");
                return EXIT_FAIL;
        }
+       map_fd = bpf_map__fd(map);
 
-       if (!prog_fd[0]) {
+       if (!prog_fd) {
                fprintf(stderr, "ERR: load_bpf_file: %s\n", strerror(errno));
                return EXIT_FAIL;
        }
@@ -512,7 +528,7 @@ int main(int argc, char **argv)
                setlocale(LC_NUMERIC, "en_US");
 
        /* User-side setup ifindex in config_map */
-       err = bpf_map_update_elem(map_fd[0], &key, &cfg, 0);
+       err = bpf_map_update_elem(map_fd, &key, &cfg, 0);
        if (err) {
                fprintf(stderr, "Store config failed (err:%d)\n", err);
                exit(EXIT_FAIL_BPF);
@@ -521,7 +537,7 @@ int main(int argc, char **argv)
        /* Remove XDP program when program is interrupted */
        signal(SIGINT, int_exit);
 
-       if (bpf_set_link_xdp_fd(ifindex, prog_fd[0], xdp_flags) < 0) {
+       if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
                fprintf(stderr, "link set xdp fd failed\n");
                return EXIT_FAIL_XDP;
        }
index f0a7872..a4ccc33 100644 (file)
@@ -18,7 +18,7 @@
 #include <unistd.h>
 #include <time.h>
 #include "bpf_load.h"
-#include "libbpf.h"
+#include <bpf/bpf.h>
 #include "bpf_util.h"
 #include "xdp_tx_iptunnel_common.h"
 
index 4b8a7cf..7fe60f6 100644 (file)
@@ -38,7 +38,7 @@
 
 #include "bpf_load.h"
 #include "bpf_util.h"
-#include "libbpf.h"
+#include <bpf/bpf.h>
 
 #include "xdpsock.h"
 
index 9e5735a..1876a74 100755 (executable)
@@ -170,7 +170,10 @@ __faddr2line() {
                echo "$file_lines" | while read -r line
                do
                        echo $line
-                       eval $(echo $line | awk -F "[ :]" '{printf("n1=%d;n2=%d;f=%s",$NF-5, $NF+5, $(NF-1))}')
+                       n=$(echo $line | sed 's/.*:\([0-9]\+\).*/\1/g')
+                       n1=$[$n-5]
+                       n2=$[$n+5]
+                       f=$(echo $line | sed 's/.*at \(.\+\):.*/\1/g')
                        awk 'NR>=strtonum("'$n1'") && NR<=strtonum("'$n2'") {printf("%d\t%s\n", NR, $0)}' $f
                done
 
index 5c508d2..6bd9358 100644 (file)
@@ -4578,6 +4578,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
 static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
 {
        struct sock *sk = sock->sk;
+       struct sk_security_struct *sksec = sk->sk_security;
        u16 family;
        int err;
 
@@ -4589,11 +4590,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
        family = sk->sk_family;
        if (family == PF_INET || family == PF_INET6) {
                char *addrp;
-               struct sk_security_struct *sksec = sk->sk_security;
                struct common_audit_data ad;
                struct lsm_network_audit net = {0,};
                struct sockaddr_in *addr4 = NULL;
                struct sockaddr_in6 *addr6 = NULL;
+               u16 family_sa = address->sa_family;
                unsigned short snum;
                u32 sid, node_perm;
 
@@ -4603,11 +4604,20 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                 * need to check address->sa_family as it is possible to have
                 * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET.
                 */
-               switch (address->sa_family) {
+               switch (family_sa) {
+               case AF_UNSPEC:
                case AF_INET:
                        if (addrlen < sizeof(struct sockaddr_in))
                                return -EINVAL;
                        addr4 = (struct sockaddr_in *)address;
+                       if (family_sa == AF_UNSPEC) {
+                               /* see __inet_bind(), we only want to allow
+                                * AF_UNSPEC if the address is INADDR_ANY
+                                */
+                               if (addr4->sin_addr.s_addr != htonl(INADDR_ANY))
+                                       goto err_af;
+                               family_sa = AF_INET;
+                       }
                        snum = ntohs(addr4->sin_port);
                        addrp = (char *)&addr4->sin_addr.s_addr;
                        break;
@@ -4619,15 +4629,14 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                        addrp = (char *)&addr6->sin6_addr.s6_addr;
                        break;
                default:
-                       /* Note that SCTP services expect -EINVAL, whereas
-                        * others expect -EAFNOSUPPORT.
-                        */
-                       if (sksec->sclass == SECCLASS_SCTP_SOCKET)
-                               return -EINVAL;
-                       else
-                               return -EAFNOSUPPORT;
+                       goto err_af;
                }
 
+               ad.type = LSM_AUDIT_DATA_NET;
+               ad.u.net = &net;
+               ad.u.net->sport = htons(snum);
+               ad.u.net->family = family_sa;
+
                if (snum) {
                        int low, high;
 
@@ -4639,10 +4648,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                                                      snum, &sid);
                                if (err)
                                        goto out;
-                               ad.type = LSM_AUDIT_DATA_NET;
-                               ad.u.net = &net;
-                               ad.u.net->sport = htons(snum);
-                               ad.u.net->family = family;
                                err = avc_has_perm(&selinux_state,
                                                   sksec->sid, sid,
                                                   sksec->sclass,
@@ -4674,16 +4679,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                        break;
                }
 
-               err = sel_netnode_sid(addrp, family, &sid);
+               err = sel_netnode_sid(addrp, family_sa, &sid);
                if (err)
                        goto out;
 
-               ad.type = LSM_AUDIT_DATA_NET;
-               ad.u.net = &net;
-               ad.u.net->sport = htons(snum);
-               ad.u.net->family = family;
-
-               if (address->sa_family == AF_INET)
+               if (family_sa == AF_INET)
                        ad.u.net->v4info.saddr = addr4->sin_addr.s_addr;
                else
                        ad.u.net->v6info.saddr = addr6->sin6_addr;
@@ -4696,6 +4696,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
        }
 out:
        return err;
+err_af:
+       /* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */
+       if (sksec->sclass == SECCLASS_SCTP_SOCKET)
+               return -EINVAL;
+       return -EAFNOSUPPORT;
 }
 
 /* This supports connect(2) and SCTP connect services such as sctp_connectx(3)
@@ -4773,7 +4778,7 @@ static int selinux_socket_connect_helper(struct socket *sock,
                ad.type = LSM_AUDIT_DATA_NET;
                ad.u.net = &net;
                ad.u.net->dport = htons(snum);
-               ad.u.net->family = sk->sk_family;
+               ad.u.net->family = address->sa_family;
                err = avc_has_perm(&selinux_state,
                                   sksec->sid, sid, sksec->sclass, perm, &ad);
                if (err)
@@ -5274,6 +5279,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname,
        while (walk_size < addrlen) {
                addr = addr_buf;
                switch (addr->sa_family) {
+               case AF_UNSPEC:
                case AF_INET:
                        len = sizeof(struct sockaddr_in);
                        break;
@@ -5281,7 +5287,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname,
                        len = sizeof(struct sockaddr_in6);
                        break;
                default:
-                       return -EAFNOSUPPORT;
+                       return -EINVAL;
                }
 
                err = -EINVAL;
index a848836..507fd52 100644 (file)
@@ -396,8 +396,7 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
            copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
                goto error;
-       if (get_user(data->owner, &data32->owner) ||
-           get_user(data->type, &data32->type))
+       if (get_user(data->owner, &data32->owner))
                goto error;
        switch (data->type) {
        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
index b0c8c79..a0c93b9 100644 (file)
@@ -2210,6 +2210,8 @@ static struct snd_pci_quirk power_save_blacklist[] = {
        SND_PCI_QUIRK(0x1849, 0x0c0c, "Asrock B85M-ITX", 0),
        /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
        SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0),
+       /* https://bugzilla.redhat.com/show_bug.cgi?id=1572975 */
+       SND_PCI_QUIRK(0x17aa, 0x36a7, "Lenovo C50 All in one", 0),
        /* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */
        SND_PCI_QUIRK(0x17aa, 0x2227, "Lenovo X1 Carbon 3rd Gen", 0),
        {}
index 2dd34dd..01a6643 100644 (file)
@@ -2363,6 +2363,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
        SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3),
        SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX),
        SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950),
+       SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950),
        SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
        SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
        SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530),
index 344d7b0..bb5ab7a 100644 (file)
@@ -967,6 +967,14 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
                }
                break;
 
+       case USB_ID(0x0d8c, 0x0103):
+               if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
+                       usb_audio_info(chip,
+                                "set volume quirk for CM102-A+/102S+\n");
+                       cval->min = -256;
+               }
+               break;
+
        case USB_ID(0x0471, 0x0101):
        case USB_ID(0x0471, 0x0104):
        case USB_ID(0x0471, 0x0105):
index 956be9f..5ed3345 100644 (file)
@@ -576,7 +576,7 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
 
        if (protocol == UAC_VERSION_1) {
                attributes = csep->bmAttributes;
-       } else {
+       } else if (protocol == UAC_VERSION_2) {
                struct uac2_iso_endpoint_descriptor *csep2 =
                        (struct uac2_iso_endpoint_descriptor *) csep;
 
@@ -585,6 +585,13 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
                /* emulate the endpoint attributes of a v1 device */
                if (csep2->bmControls & UAC2_CONTROL_PITCH)
                        attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL;
+       } else { /* UAC_VERSION_3 */
+               struct uac3_iso_endpoint_descriptor *csep3 =
+                       (struct uac3_iso_endpoint_descriptor *) csep;
+
+               /* emulate the endpoint attributes of a v1 device */
+               if (le32_to_cpu(csep3->bmControls) & UAC2_CONTROL_PITCH)
+                       attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL;
        }
 
        return attributes;
index 2ba95d6..caae484 100644 (file)
@@ -195,6 +195,12 @@ struct kvm_arch_memory_slot {
 #define KVM_REG_ARM_VFP_FPINST         0x1009
 #define KVM_REG_ARM_VFP_FPINST2                0x100A
 
+/* KVM-as-firmware specific pseudo-registers */
+#define KVM_REG_ARM_FW                 (0x0014 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_FW_REG(r)          (KVM_REG_ARM | KVM_REG_SIZE_U64 | \
+                                        KVM_REG_ARM_FW | ((r) & 0xffff))
+#define KVM_REG_ARM_PSCI_VERSION       KVM_REG_ARM_FW_REG(0)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR      0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
index 9abbf30..04b3256 100644 (file)
@@ -206,6 +206,12 @@ struct kvm_arch_memory_slot {
 #define KVM_REG_ARM_TIMER_CNT          ARM64_SYS_REG(3, 3, 14, 3, 2)
 #define KVM_REG_ARM_TIMER_CVAL         ARM64_SYS_REG(3, 3, 14, 0, 2)
 
+/* KVM-as-firmware specific pseudo-registers */
+#define KVM_REG_ARM_FW                 (0x0014 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM_FW_REG(r)          (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
+                                        KVM_REG_ARM_FW | ((r) & 0xffff))
+#define KVM_REG_ARM_PSCI_VERSION       KVM_REG_ARM_FW_REG(0)
+
 /* Device Control API: ARM VGIC */
 #define KVM_DEV_ARM_VGIC_GRP_ADDR      0
 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
index d554c11..578793e 100644 (file)
 #define X86_FEATURE_AVX512_VPOPCNTDQ   (16*32+14) /* POPCNT for vectors of DW/QW */
 #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 */
 
 /* AMD-defined CPU features, CPUID level 0x80000007 (EBX), word 17 */
 #define X86_FEATURE_OVERFLOW_RECOV     (17*32+ 0) /* MCA overflow recovery support */
diff --git a/tools/bpf/bpftool/.gitignore b/tools/bpf/bpftool/.gitignore
new file mode 100644 (file)
index 0000000..d7e678c
--- /dev/null
@@ -0,0 +1,3 @@
+*.d
+bpftool
+FEATURE-DUMP.bpftool
index af6766e..097b1a5 100644 (file)
@@ -66,6 +66,7 @@ static const char * const map_type_name[] = {
        [BPF_MAP_TYPE_DEVMAP]           = "devmap",
        [BPF_MAP_TYPE_SOCKMAP]          = "sockmap",
        [BPF_MAP_TYPE_CPUMAP]           = "cpumap",
+       [BPF_MAP_TYPE_SOCKHASH]         = "sockhash",
 };
 
 static bool map_is_per_cpu(__u32 type)
index c5a2ced..1832100 100644 (file)
@@ -39,6 +39,7 @@ struct event_ring_info {
 
 struct perf_event_sample {
        struct perf_event_header header;
+       u64 time;
        __u32 size;
        unsigned char data[];
 };
@@ -49,25 +50,18 @@ static void int_exit(int signo)
        stop = true;
 }
 
-static void
-print_bpf_output(struct event_ring_info *ring, struct perf_event_sample *e)
+static enum bpf_perf_event_ret print_bpf_output(void *event, void *priv)
 {
+       struct event_ring_info *ring = priv;
+       struct perf_event_sample *e = event;
        struct {
                struct perf_event_header header;
                __u64 id;
                __u64 lost;
-       } *lost = (void *)e;
-       struct timespec ts;
-
-       if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
-               perror("Can't read clock for timestamp");
-               return;
-       }
+       } *lost = event;
 
        if (json_output) {
                jsonw_start_object(json_wtr);
-               jsonw_name(json_wtr, "timestamp");
-               jsonw_uint(json_wtr, ts.tv_sec * 1000000000ull + ts.tv_nsec);
                jsonw_name(json_wtr, "type");
                jsonw_uint(json_wtr, e->header.type);
                jsonw_name(json_wtr, "cpu");
@@ -75,6 +69,8 @@ print_bpf_output(struct event_ring_info *ring, struct perf_event_sample *e)
                jsonw_name(json_wtr, "index");
                jsonw_uint(json_wtr, ring->key);
                if (e->header.type == PERF_RECORD_SAMPLE) {
+                       jsonw_name(json_wtr, "timestamp");
+                       jsonw_uint(json_wtr, e->time);
                        jsonw_name(json_wtr, "data");
                        print_data_json(e->data, e->size);
                } else if (e->header.type == PERF_RECORD_LOST) {
@@ -89,8 +85,8 @@ print_bpf_output(struct event_ring_info *ring, struct perf_event_sample *e)
                jsonw_end_object(json_wtr);
        } else {
                if (e->header.type == PERF_RECORD_SAMPLE) {
-                       printf("== @%ld.%ld CPU: %d index: %d =====\n",
-                              (long)ts.tv_sec, ts.tv_nsec,
+                       printf("== @%lld.%09lld CPU: %d index: %d =====\n",
+                              e->time / 1000000000ULL, e->time % 1000000000ULL,
                               ring->cpu, ring->key);
                        fprint_hex(stdout, e->data, e->size, " ");
                        printf("\n");
@@ -101,60 +97,23 @@ print_bpf_output(struct event_ring_info *ring, struct perf_event_sample *e)
                               e->header.type, e->header.size);
                }
        }
+
+       return LIBBPF_PERF_EVENT_CONT;
 }
 
 static void
 perf_event_read(struct event_ring_info *ring, void **buf, size_t *buf_len)
 {
-       volatile struct perf_event_mmap_page *header = ring->mem;
-       __u64 buffer_size = MMAP_PAGE_CNT * get_page_size();
-       __u64 data_tail = header->data_tail;
-       __u64 data_head = header->data_head;
-       void *base, *begin, *end;
-
-       asm volatile("" ::: "memory"); /* in real code it should be smp_rmb() */
-       if (data_head == data_tail)
-               return;
-
-       base = ((char *)header) + get_page_size();
-
-       begin = base + data_tail % buffer_size;
-       end = base + data_head % buffer_size;
-
-       while (begin != end) {
-               struct perf_event_sample *e;
-
-               e = begin;
-               if (begin + e->header.size > base + buffer_size) {
-                       long len = base + buffer_size - begin;
-
-                       if (*buf_len < e->header.size) {
-                               free(*buf);
-                               *buf = malloc(e->header.size);
-                               if (!*buf) {
-                                       fprintf(stderr,
-                                               "can't allocate memory");
-                                       stop = true;
-                                       return;
-                               }
-                               *buf_len = e->header.size;
-                       }
-
-                       memcpy(*buf, begin, len);
-                       memcpy(*buf + len, base, e->header.size - len);
-                       e = (void *)*buf;
-                       begin = base + e->header.size - len;
-               } else if (begin + e->header.size == base + buffer_size) {
-                       begin = base;
-               } else {
-                       begin += e->header.size;
-               }
-
-               print_bpf_output(ring, e);
+       enum bpf_perf_event_ret ret;
+
+       ret = bpf_perf_event_read_simple(ring->mem,
+                                        MMAP_PAGE_CNT * get_page_size(),
+                                        get_page_size(), buf, buf_len,
+                                        print_bpf_output, ring);
+       if (ret != LIBBPF_PERF_EVENT_CONT) {
+               fprintf(stderr, "perf read loop failed with %d\n", ret);
+               stop = true;
        }
-
-       __sync_synchronize(); /* smp_mb() */
-       header->data_tail = data_head;
 }
 
 static int perf_mmap_size(void)
@@ -185,7 +144,7 @@ static void perf_event_unmap(void *mem)
 static int bpf_perf_event_open(int map_fd, int key, int cpu)
 {
        struct perf_event_attr attr = {
-               .sample_type = PERF_SAMPLE_RAW,
+               .sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME,
                .type = PERF_TYPE_SOFTWARE,
                .config = PERF_COUNT_SW_BPF_OUTPUT,
        };
index b21b586..1738c03 100644 (file)
@@ -6,8 +6,9 @@
 #include <stdbool.h>
 
 #define spinlock_t             pthread_mutex_t
-#define DEFINE_SPINLOCK(x)     pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER;
+#define DEFINE_SPINLOCK(x)     pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER
 #define __SPIN_LOCK_UNLOCKED(x)        (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER
+#define spin_lock_init(x)      pthread_mutex_init(x, NULL)
 
 #define spin_lock_irqsave(x, f)                (void)f, pthread_mutex_lock(x)
 #define spin_unlock_irqrestore(x, f)   (void)f, pthread_mutex_unlock(x)
diff --git a/tools/include/uapi/asm/bitsperlong.h b/tools/include/uapi/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..8dd6aef
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#if defined(__i386__) || defined(__x86_64__)
+#include "../../arch/x86/include/uapi/asm/bitsperlong.h"
+#elif defined(__aarch64__)
+#include "../../arch/arm64/include/uapi/asm/bitsperlong.h"
+#elif defined(__powerpc__)
+#include "../../arch/powerpc/include/uapi/asm/bitsperlong.h"
+#elif defined(__s390__)
+#include "../../arch/s390/include/uapi/asm/bitsperlong.h"
+#elif defined(__sparc__)
+#include "../../arch/sparc/include/uapi/asm/bitsperlong.h"
+#elif defined(__mips__)
+#include "../../arch/mips/include/uapi/asm/bitsperlong.h"
+#elif defined(__ia64__)
+#include "../../arch/ia64/include/uapi/asm/bitsperlong.h"
+#else
+#include <asm-generic/bitsperlong.h>
+#endif
diff --git a/tools/include/uapi/asm/errno.h b/tools/include/uapi/asm/errno.h
new file mode 100644 (file)
index 0000000..ce3c594
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#if defined(__i386__) || defined(__x86_64__)
+#include "../../arch/x86/include/uapi/asm/errno.h"
+#elif defined(__powerpc__)
+#include "../../arch/powerpc/include/uapi/asm/errno.h"
+#elif defined(__sparc__)
+#include "../../arch/sparc/include/uapi/asm/errno.h"
+#elif defined(__alpha__)
+#include "../../arch/alpha/include/uapi/asm/errno.h"
+#elif defined(__mips__)
+#include "../../arch/mips/include/uapi/asm/errno.h"
+#elif defined(__ia64__)
+#include "../../arch/ia64/include/uapi/asm/errno.h"
+#elif defined(__xtensa__)
+#include "../../arch/xtensa/include/uapi/asm/errno.h"
+#else
+#include <asm-generic/errno.h>
+#endif
index 83a95ae..d94d333 100644 (file)
@@ -96,6 +96,7 @@ enum bpf_cmd {
        BPF_PROG_QUERY,
        BPF_RAW_TRACEPOINT_OPEN,
        BPF_BTF_LOAD,
+       BPF_BTF_GET_FD_BY_ID,
 };
 
 enum bpf_map_type {
@@ -116,6 +117,8 @@ enum bpf_map_type {
        BPF_MAP_TYPE_DEVMAP,
        BPF_MAP_TYPE_SOCKMAP,
        BPF_MAP_TYPE_CPUMAP,
+       BPF_MAP_TYPE_XSKMAP,
+       BPF_MAP_TYPE_SOCKHASH,
 };
 
 enum bpf_prog_type {
@@ -343,6 +346,7 @@ union bpf_attr {
                        __u32           start_id;
                        __u32           prog_id;
                        __u32           map_id;
+                       __u32           btf_id;
                };
                __u32           next_id;
                __u32           open_flags;
@@ -1825,6 +1829,79 @@ union bpf_attr {
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
+ * int bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags)
+ *     Description
+ *             Do FIB lookup in kernel tables using parameters in *params*.
+ *             If lookup is successful and result shows packet is to be
+ *             forwarded, the neighbor tables are searched for the nexthop.
+ *             If successful (ie., FIB lookup shows forwarding and nexthop
+ *             is resolved), the nexthop address is returned in ipv4_dst,
+ *             ipv6_dst or mpls_out based on family, smac is set to mac
+ *             address of egress device, dmac is set to nexthop mac address,
+ *             rt_metric is set to metric from route.
+ *
+ *             *plen* argument is the size of the passed in struct.
+ *             *flags* argument can be one or more BPF_FIB_LOOKUP_ flags:
+ *
+ *             **BPF_FIB_LOOKUP_DIRECT** means do a direct table lookup vs
+ *             full lookup using FIB rules
+ *             **BPF_FIB_LOOKUP_OUTPUT** means do 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
+ *             Egress device index on success, 0 if packet needs to continue
+ *             up the stack for further processing or a negative error in case
+ *             of failure.
+ *
+ * int bpf_sock_hash_update(struct bpf_sock_ops_kern *skops, struct bpf_map *map, void *key, u64 flags)
+ *     Description
+ *             Add an entry to, or update a sockhash *map* referencing sockets.
+ *             The *skops* is used as a new value for the entry associated to
+ *             *key*. *flags* is one of:
+ *
+ *             **BPF_NOEXIST**
+ *                     The entry for *key* must not exist in the map.
+ *             **BPF_EXIST**
+ *                     The entry for *key* must already exist in the map.
+ *             **BPF_ANY**
+ *                     No condition on the existence of the entry for *key*.
+ *
+ *             If the *map* has eBPF programs (parser and verdict), those will
+ *             be inherited by the socket being added. If the socket is
+ *             already attached to eBPF programs, this results in an error.
+ *     Return
+ *             0 on success, or a negative error in case of failure.
+ *
+ * int bpf_msg_redirect_hash(struct sk_msg_buff *msg, struct bpf_map *map, void *key, u64 flags)
+ *     Description
+ *             This helper is used in programs implementing policies at the
+ *             socket level. If the message *msg* is allowed to pass (i.e. if
+ *             the verdict eBPF program returns **SK_PASS**), redirect it to
+ *             the socket referenced by *map* (of type
+ *             **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
+ *             egress interfaces can be used for redirection. The
+ *             **BPF_F_INGRESS** value in *flags* is used to make the
+ *             distinction (ingress path is selected if the flag is present,
+ *             egress path otherwise). This is the only flag supported for now.
+ *     Return
+ *             **SK_PASS** on success, or **SK_DROP** on error.
+ *
+ * int bpf_sk_redirect_hash(struct sk_buff *skb, struct bpf_map *map, void *key, u64 flags)
+ *     Description
+ *             This helper is used in programs implementing policies at the
+ *             skb socket level. If the sk_buff *skb* is allowed to pass (i.e.
+ *             if the verdeict eBPF program returns **SK_PASS**), redirect it
+ *             to the socket referenced by *map* (of type
+ *             **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
+ *             egress interfaces can be used for redirection. The
+ *             **BPF_F_INGRESS** value in *flags* is used to make the
+ *             distinction (ingress path is selected if the flag is present,
+ *             egress otherwise). This is the only flag supported for now.
+ *     Return
+ *             **SK_PASS** on success, or **SK_DROP** on error.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -1895,7 +1972,11 @@ union bpf_attr {
        FN(xdp_adjust_tail),            \
        FN(skb_get_xfrm_state),         \
        FN(get_stack),                  \
-       FN(skb_load_bytes_relative),
+       FN(skb_load_bytes_relative),    \
+       FN(fib_lookup),                 \
+       FN(sock_hash_update),           \
+       FN(msg_redirect_hash),          \
+       FN(sk_redirect_hash),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -2129,6 +2210,15 @@ struct bpf_map_info {
        __u32 ifindex;
        __u64 netns_dev;
        __u64 netns_ino;
+       __u32 btf_id;
+       __u32 btf_key_id;
+       __u32 btf_value_id;
+} __attribute__((aligned(8)));
+
+struct bpf_btf_info {
+       __aligned_u64 btf;
+       __u32 btf_size;
+       __u32 id;
 } __attribute__((aligned(8)));
 
 /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
@@ -2309,4 +2399,55 @@ struct bpf_raw_tracepoint_args {
        __u64 args[0];
 };
 
+/* DIRECT:  Skip the FIB rules and go to FIB table associated with device
+ * OUTPUT:  Do lookup from egress perspective; default is ingress
+ */
+#define BPF_FIB_LOOKUP_DIRECT  BIT(0)
+#define BPF_FIB_LOOKUP_OUTPUT  BIT(1)
+
+struct bpf_fib_lookup {
+       /* input */
+       __u8    family;   /* network family, AF_INET, AF_INET6, AF_MPLS */
+
+       /* set if lookup is to consider L4 data - e.g., FIB rules */
+       __u8    l4_protocol;
+       __be16  sport;
+       __be16  dport;
+
+       /* total length of packet from network header - used for MTU check */
+       __u16   tot_len;
+       __u32   ifindex;  /* L3 device index for lookup */
+
+       union {
+               /* inputs to lookup */
+               __u8    tos;            /* AF_INET  */
+               __be32  flowlabel;      /* AF_INET6 */
+
+               /* output: metric of fib result */
+               __u32 rt_metric;
+       };
+
+       union {
+               __be32          mpls_in;
+               __be32          ipv4_src;
+               __u32           ipv6_src[4];  /* in6_addr; network order */
+       };
+
+       /* input to bpf_fib_lookup, *dst is destination address.
+        * output: bpf_fib_lookup sets to gateway address
+        */
+       union {
+               /* return for MPLS lookups */
+               __be32          mpls_out[4];  /* support up to 4 labels */
+               __be32          ipv4_dst;
+               __u32           ipv6_dst[4];  /* in6_addr; network order */
+       };
+
+       /* output */
+       __be16  h_vlan_proto;
+       __be16  h_vlan_TCI;
+       __u8    smac[6];     /* ETH_ALEN */
+       __u8    dmac[6];     /* ETH_ALEN */
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
index 1065006..b02c41e 100644 (file)
@@ -676,6 +676,13 @@ struct kvm_ioeventfd {
        __u8  pad[36];
 };
 
+#define KVM_X86_DISABLE_EXITS_MWAIT          (1 << 0)
+#define KVM_X86_DISABLE_EXITS_HTL            (1 << 1)
+#define KVM_X86_DISABLE_EXITS_PAUSE          (1 << 2)
+#define KVM_X86_DISABLE_VALID_EXITS          (KVM_X86_DISABLE_EXITS_MWAIT | \
+                                              KVM_X86_DISABLE_EXITS_HTL | \
+                                              KVM_X86_DISABLE_EXITS_PAUSE)
+
 /* for KVM_ENABLE_CAP */
 struct kvm_enable_cap {
        /* in */
index e6d5f8d..f3fab4a 100644 (file)
@@ -69,7 +69,7 @@ FEATURE_USER = .libbpf
 FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf
 FEATURE_DISPLAY = libelf bpf
 
-INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi
+INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi -I$(srctree)/tools/perf
 FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)
 
 check_feat := 1
index 76b36cc..6a8a000 100644 (file)
@@ -91,6 +91,7 @@ int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr)
        attr.btf_fd = create_attr->btf_fd;
        attr.btf_key_id = create_attr->btf_key_id;
        attr.btf_value_id = create_attr->btf_value_id;
+       attr.map_ifindex = create_attr->map_ifindex;
 
        return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
 }
@@ -201,6 +202,7 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
        attr.log_size = 0;
        attr.log_level = 0;
        attr.kern_version = load_attr->kern_version;
+       attr.prog_ifindex = load_attr->prog_ifindex;
        memcpy(attr.prog_name, load_attr->name,
               min(name_len, BPF_OBJ_NAME_LEN - 1));
 
@@ -458,6 +460,16 @@ int bpf_map_get_fd_by_id(__u32 id)
        return sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
 }
 
+int bpf_btf_get_fd_by_id(__u32 id)
+{
+       union bpf_attr attr;
+
+       bzero(&attr, sizeof(attr));
+       attr.btf_id = id;
+
+       return sys_bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
 int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len)
 {
        union bpf_attr attr;
index 553b11a..15bff77 100644 (file)
@@ -38,6 +38,7 @@ struct bpf_create_map_attr {
        __u32 btf_fd;
        __u32 btf_key_id;
        __u32 btf_value_id;
+       __u32 map_ifindex;
 };
 
 int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr);
@@ -64,6 +65,7 @@ struct bpf_load_program_attr {
        size_t insns_cnt;
        const char *license;
        __u32 kern_version;
+       __u32 prog_ifindex;
 };
 
 /* Recommend log buffer size */
@@ -98,6 +100,7 @@ int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
 int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
 int bpf_prog_get_fd_by_id(__u32 id);
 int bpf_map_get_fd_by_id(__u32 id);
+int bpf_btf_get_fd_by_id(__u32 id);
 int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len);
 int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
                   __u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt);
index 7bcdca1..cbdf34a 100644 (file)
@@ -31,6 +31,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <perf-sys.h>
 #include <asm/unistd.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
@@ -177,6 +178,7 @@ struct bpf_program {
        /* Index in elf obj file, for relocation use. */
        int idx;
        char *name;
+       int prog_ifindex;
        char *section_name;
        struct bpf_insn *insns;
        size_t insns_cnt, main_prog_cnt;
@@ -212,6 +214,7 @@ struct bpf_map {
        int fd;
        char *name;
        size_t offset;
+       int map_ifindex;
        struct bpf_map_def def;
        uint32_t btf_key_id;
        uint32_t btf_value_id;
@@ -1090,6 +1093,7 @@ bpf_object__create_maps(struct bpf_object *obj)
                int *pfd = &map->fd;
 
                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;
                create_attr.key_size = def->key_size;
@@ -1272,7 +1276,7 @@ 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)
+            char *license, u32 kern_version, int *pfd, int prog_ifindex)
 {
        struct bpf_load_program_attr load_attr;
        char *log_buf;
@@ -1286,6 +1290,7 @@ load_program(enum bpf_prog_type type, enum bpf_attach_type expected_attach_type,
        load_attr.insns_cnt = insns_cnt;
        load_attr.license = license;
        load_attr.kern_version = kern_version;
+       load_attr.prog_ifindex = prog_ifindex;
 
        if (!load_attr.insns || !load_attr.insns_cnt)
                return -EINVAL;
@@ -1367,7 +1372,8 @@ bpf_program__load(struct bpf_program *prog,
                }
                err = load_program(prog->type, prog->expected_attach_type,
                                   prog->name, prog->insns, prog->insns_cnt,
-                                  license, kern_version, &fd);
+                                  license, kern_version, &fd,
+                                  prog->prog_ifindex);
                if (!err)
                        prog->instances.fds[0] = fd;
                goto out;
@@ -1398,7 +1404,8 @@ bpf_program__load(struct bpf_program *prog,
                err = load_program(prog->type, prog->expected_attach_type,
                                   prog->name, result.new_insn_ptr,
                                   result.new_insn_cnt,
-                                  license, kern_version, &fd);
+                                  license, kern_version, &fd,
+                                  prog->prog_ifindex);
 
                if (err) {
                        pr_warning("Loading the %dth instance of program '%s' failed\n",
@@ -1437,9 +1444,37 @@ bpf_object__load_progs(struct bpf_object *obj)
        return 0;
 }
 
-static int bpf_object__validate(struct bpf_object *obj)
+static bool bpf_prog_type__needs_kver(enum bpf_prog_type type)
+{
+       switch (type) {
+       case BPF_PROG_TYPE_SOCKET_FILTER:
+       case BPF_PROG_TYPE_SCHED_CLS:
+       case BPF_PROG_TYPE_SCHED_ACT:
+       case BPF_PROG_TYPE_XDP:
+       case BPF_PROG_TYPE_CGROUP_SKB:
+       case BPF_PROG_TYPE_CGROUP_SOCK:
+       case BPF_PROG_TYPE_LWT_IN:
+       case BPF_PROG_TYPE_LWT_OUT:
+       case BPF_PROG_TYPE_LWT_XMIT:
+       case BPF_PROG_TYPE_SOCK_OPS:
+       case BPF_PROG_TYPE_SK_SKB:
+       case BPF_PROG_TYPE_CGROUP_DEVICE:
+       case BPF_PROG_TYPE_SK_MSG:
+       case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
+               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:
+       default:
+               return true;
+       }
+}
+
+static int bpf_object__validate(struct bpf_object *obj, bool needs_kver)
 {
-       if (obj->kern_version == 0) {
+       if (needs_kver && obj->kern_version == 0) {
                pr_warning("%s doesn't provide kernel version\n",
                           obj->path);
                return -LIBBPF_ERRNO__KVERSION;
@@ -1448,7 +1483,8 @@ static int bpf_object__validate(struct bpf_object *obj)
 }
 
 static struct bpf_object *
-__bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
+__bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz,
+                  bool needs_kver)
 {
        struct bpf_object *obj;
        int err;
@@ -1466,7 +1502,7 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
        CHECK_ERR(bpf_object__check_endianness(obj), err, out);
        CHECK_ERR(bpf_object__elf_collect(obj), err, out);
        CHECK_ERR(bpf_object__collect_reloc(obj), err, out);
-       CHECK_ERR(bpf_object__validate(obj), err, out);
+       CHECK_ERR(bpf_object__validate(obj, needs_kver), err, out);
 
        bpf_object__elf_finish(obj);
        return obj;
@@ -1483,7 +1519,7 @@ struct bpf_object *bpf_object__open(const char *path)
 
        pr_debug("loading %s\n", path);
 
-       return __bpf_object__open(path, NULL, 0);
+       return __bpf_object__open(path, NULL, 0, true);
 }
 
 struct bpf_object *bpf_object__open_buffer(void *obj_buf,
@@ -1506,7 +1542,7 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
        pr_debug("loading object '%s' from buffer\n",
                 name);
 
-       return __bpf_object__open(name, obj_buf, obj_buf_sz);
+       return __bpf_object__open(name, obj_buf, obj_buf_sz, true);
 }
 
 int bpf_object__unload(struct bpf_object *obj)
@@ -2158,14 +2194,18 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
        enum bpf_attach_type expected_attach_type;
        enum bpf_prog_type prog_type;
        struct bpf_object *obj;
+       struct bpf_map *map;
        int section_idx;
        int err;
 
        if (!attr)
                return -EINVAL;
+       if (!attr->file)
+               return -EINVAL;
 
-       obj = bpf_object__open(attr->file);
-       if (IS_ERR(obj))
+       obj = __bpf_object__open(attr->file, NULL, 0,
+                                bpf_prog_type__needs_kver(attr->prog_type));
+       if (IS_ERR_OR_NULL(obj))
                return -ENOENT;
 
        bpf_object__for_each_program(prog, obj) {
@@ -2174,6 +2214,7 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
                 * section name.
                 */
                prog_type = attr->prog_type;
+               prog->prog_ifindex = attr->ifindex;
                expected_attach_type = attr->expected_attach_type;
                if (prog_type == BPF_PROG_TYPE_UNSPEC) {
                        section_idx = bpf_program__identify_section(prog);
@@ -2194,6 +2235,10 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
                        first_prog = prog;
        }
 
+       bpf_map__for_each(map, obj) {
+               map->map_ifindex = attr->ifindex;
+       }
+
        if (!first_prog) {
                pr_warning("object file doesn't contain bpf program\n");
                bpf_object__close(obj);
@@ -2210,3 +2255,63 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
        *prog_fd = bpf_program__fd(first_prog);
        return 0;
 }
+
+enum bpf_perf_event_ret
+bpf_perf_event_read_simple(void *mem, unsigned long size,
+                          unsigned long page_size, void **buf, size_t *buf_len,
+                          bpf_perf_event_print_t fn, void *priv)
+{
+       volatile struct perf_event_mmap_page *header = mem;
+       __u64 data_tail = header->data_tail;
+       __u64 data_head = header->data_head;
+       void *base, *begin, *end;
+       int ret;
+
+       asm volatile("" ::: "memory"); /* in real code it should be smp_rmb() */
+       if (data_head == data_tail)
+               return LIBBPF_PERF_EVENT_CONT;
+
+       base = ((char *)header) + page_size;
+
+       begin = base + data_tail % size;
+       end = base + data_head % size;
+
+       while (begin != end) {
+               struct perf_event_header *ehdr;
+
+               ehdr = begin;
+               if (begin + ehdr->size > base + size) {
+                       long len = base + size - begin;
+
+                       if (*buf_len < ehdr->size) {
+                               free(*buf);
+                               *buf = malloc(ehdr->size);
+                               if (!*buf) {
+                                       ret = LIBBPF_PERF_EVENT_ERROR;
+                                       break;
+                               }
+                               *buf_len = ehdr->size;
+                       }
+
+                       memcpy(*buf, begin, len);
+                       memcpy(*buf + len, base, ehdr->size - len);
+                       ehdr = (void *)*buf;
+                       begin = base + ehdr->size - len;
+               } else if (begin + ehdr->size == base + size) {
+                       begin = base;
+               } else {
+                       begin += ehdr->size;
+               }
+
+               ret = fn(ehdr, priv);
+               if (ret != LIBBPF_PERF_EVENT_CONT)
+                       break;
+
+               data_tail += ehdr->size;
+       }
+
+       __sync_synchronize(); /* smp_mb() */
+       header->data_tail = data_tail;
+
+       return ret;
+}
index 197f9ce..cd3fd8d 100644 (file)
@@ -52,8 +52,8 @@ enum libbpf_errno {
 int libbpf_strerror(int err, char *buf, size_t size);
 
 /*
- * In include/linux/compiler-gcc.h, __printf is defined. However
- * it should be better if libbpf.h doesn't depend on Linux header file.
+ * __printf is defined in include/linux/compiler-gcc.h. However,
+ * it would be better if libbpf.h didn't depend on Linux header files.
  * So instead of __printf, here we use gcc attribute directly.
  */
 typedef int (*libbpf_print_fn_t)(const char *, ...)
@@ -92,7 +92,7 @@ int bpf_object__set_priv(struct bpf_object *obj, void *priv,
                         bpf_object_clear_priv_t clear_priv);
 void *bpf_object__priv(struct bpf_object *prog);
 
-/* Accessors of bpf_program. */
+/* Accessors of bpf_program */
 struct bpf_program;
 struct bpf_program *bpf_program__next(struct bpf_program *prog,
                                      struct bpf_object *obj);
@@ -121,28 +121,28 @@ struct bpf_insn;
 
 /*
  * Libbpf allows callers to adjust BPF programs before being loaded
- * into kernel. One program in an object file can be transform into
- * multiple variants to be attached to different code.
+ * into kernel. One program in an object file can be transformed into
+ * multiple variants to be attached to different hooks.
  *
  * bpf_program_prep_t, bpf_program__set_prep and bpf_program__nth_fd
- * are APIs for this propose.
+ * form an API for this purpose.
  *
  * - bpf_program_prep_t:
- *   It defines 'preprocessor', which is a caller defined function
+ *   Defines a 'preprocessor', which is a caller defined function
  *   passed to libbpf through bpf_program__set_prep(), and will be
  *   called before program is loaded. The processor should adjust
- *   the program one time for each instances according to the number
+ *   the program one time for each instance according to the instance id
  *   passed to it.
  *
  * - bpf_program__set_prep:
- *   Attachs a preprocessor to a BPF program. The number of instances
- *   whould be created is also passed through this function.
+ *   Attaches a preprocessor to a BPF program. The number of instances
+ *   that should be created is also passed through this function.
  *
  * - bpf_program__nth_fd:
- *   After the program is loaded, get resuling fds from bpf program for
- *   each instances.
+ *   After the program is loaded, get resulting FD of a given instance
+ *   of the BPF program.
  *
- * If bpf_program__set_prep() is not used, the program whould be loaded
+ * If bpf_program__set_prep() is not used, the program would be loaded
  * without adjustment during bpf_object__load(). The program has only
  * one instance. In this case bpf_program__fd(prog) is equal to
  * bpf_program__nth_fd(prog, 0).
@@ -156,7 +156,7 @@ struct bpf_prog_prep_result {
        struct bpf_insn *new_insn_ptr;
        int new_insn_cnt;
 
-       /* If not NULL, result fd is set to it */
+       /* If not NULL, result FD is written to it. */
        int *pfd;
 };
 
@@ -169,8 +169,8 @@ struct bpf_prog_prep_result {
  *  - res:     Output parameter, result of transformation.
  *
  * Return value:
- *  - Zero: pre-processing success.
- *  - Non-zero: pre-processing, stop loading.
+ *  - Zero:    pre-processing success.
+ *  - Non-zero:        pre-processing error, stop loading.
  */
 typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
                                  struct bpf_insn *insns, int insns_cnt,
@@ -182,7 +182,7 @@ int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
 int bpf_program__nth_fd(struct bpf_program *prog, int n);
 
 /*
- * Adjust type of bpf program. Default is kprobe.
+ * Adjust type of BPF program. Default is kprobe.
  */
 int bpf_program__set_socket_filter(struct bpf_program *prog);
 int bpf_program__set_tracepoint(struct bpf_program *prog);
@@ -206,10 +206,10 @@ bool bpf_program__is_xdp(struct bpf_program *prog);
 bool bpf_program__is_perf_event(struct bpf_program *prog);
 
 /*
- * We don't need __attribute__((packed)) now since it is
- * unnecessary for 'bpf_map_def' because they are all aligned.
- * In addition, using it will trigger -Wpacked warning message,
- * and will be treated as an error due to -Werror.
+ * No need for __attribute__((packed)), all members of 'bpf_map_def'
+ * are all aligned.  In addition, using __attribute__((packed))
+ * would trigger a -Wpacked warning message, and lead to an error
+ * if -Werror is set.
  */
 struct bpf_map_def {
        unsigned int type;
@@ -220,8 +220,8 @@ struct bpf_map_def {
 };
 
 /*
- * There is another 'struct bpf_map' in include/linux/map.h. However,
- * it is not a uapi header so no need to consider name clash.
+ * The 'struct bpf_map' in include/linux/bpf.h is internal to the kernel,
+ * so no need to worry about a name clash.
  */
 struct bpf_map;
 struct bpf_map *
@@ -229,7 +229,7 @@ bpf_object__find_map_by_name(struct bpf_object *obj, const char *name);
 
 /*
  * Get bpf_map through the offset of corresponding struct bpf_map_def
- * in the bpf object file.
+ * in the BPF object file.
  */
 struct bpf_map *
 bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset);
@@ -259,6 +259,7 @@ struct bpf_prog_load_attr {
        const char *file;
        enum bpf_prog_type prog_type;
        enum bpf_attach_type expected_attach_type;
+       int ifindex;
 };
 
 int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
@@ -267,4 +268,17 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type,
                  struct bpf_object **pobj, int *prog_fd);
 
 int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
+
+enum bpf_perf_event_ret {
+       LIBBPF_PERF_EVENT_DONE  = 0,
+       LIBBPF_PERF_EVENT_ERROR = -1,
+       LIBBPF_PERF_EVENT_CONT  = -2,
+};
+
+typedef enum bpf_perf_event_ret (*bpf_perf_event_print_t)(void *event,
+                                                         void *priv);
+int bpf_perf_event_read_simple(void *mem, unsigned long size,
+                              unsigned long page_size,
+                              void **buf, size_t *buf_len,
+                              bpf_perf_event_print_t fn, void *priv);
 #endif
index b3e32b0..c2c01f8 100644 (file)
@@ -208,4 +208,22 @@ static inline int insn_offset_immediate(struct insn *insn)
        return insn_offset_displacement(insn) + insn->displacement.nbytes;
 }
 
+#define POP_SS_OPCODE 0x1f
+#define MOV_SREG_OPCODE 0x8e
+
+/*
+ * Intel SDM Vol.3A 6.8.3 states;
+ * "Any single-step trap that would be delivered following the MOV to SS
+ * instruction or POP to SS instruction (because EFLAGS.TF is 1) is
+ * suppressed."
+ * This function returns true if @insn is MOV SS or POP SS. On these
+ * instructions, single stepping is suppressed.
+ */
+static inline int insn_masking_exception(struct insn *insn)
+{
+       return insn->opcode.bytes[0] == POP_SS_OPCODE ||
+               (insn->opcode.bytes[0] == MOV_SREG_OPCODE &&
+                X86_MODRM_REG(insn->modrm.bytes[0]) == 2);
+}
+
 #endif /* _ASM_X86_INSN_H */
index 5409f6f..3a31b23 100644 (file)
@@ -59,6 +59,31 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file,
        return next;
 }
 
+static struct instruction *next_insn_same_func(struct objtool_file *file,
+                                              struct instruction *insn)
+{
+       struct instruction *next = list_next_entry(insn, list);
+       struct symbol *func = insn->func;
+
+       if (!func)
+               return NULL;
+
+       if (&next->list != &file->insn_list && next->func == func)
+               return next;
+
+       /* Check if we're already in the subfunction: */
+       if (func == func->cfunc)
+               return NULL;
+
+       /* Move to the subfunction: */
+       return find_insn(file, func->cfunc->sec, func->cfunc->offset);
+}
+
+#define func_for_each_insn_all(file, func, insn)                       \
+       for (insn = find_insn(file, func->sec, func->offset);           \
+            insn;                                                      \
+            insn = next_insn_same_func(file, insn))
+
 #define func_for_each_insn(file, func, insn)                           \
        for (insn = find_insn(file, func->sec, func->offset);           \
             insn && &insn->list != &file->insn_list &&                 \
@@ -149,10 +174,14 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
                        if (!strcmp(func->name, global_noreturns[i]))
                                return 1;
 
-       if (!func->sec)
+       if (!func->len)
                return 0;
 
-       func_for_each_insn(file, func, insn) {
+       insn = find_insn(file, func->sec, func->offset);
+       if (!insn->func)
+               return 0;
+
+       func_for_each_insn_all(file, func, insn) {
                empty = false;
 
                if (insn->type == INSN_RETURN)
@@ -167,35 +196,28 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
         * case, the function's dead-end status depends on whether the target
         * of the sibling call returns.
         */
-       func_for_each_insn(file, func, insn) {
-               if (insn->sec != func->sec ||
-                   insn->offset >= func->offset + func->len)
-                       break;
-
+       func_for_each_insn_all(file, func, insn) {
                if (insn->type == INSN_JUMP_UNCONDITIONAL) {
                        struct instruction *dest = insn->jump_dest;
-                       struct symbol *dest_func;
 
                        if (!dest)
                                /* sibling call to another file */
                                return 0;
 
-                       if (dest->sec != func->sec ||
-                           dest->offset < func->offset ||
-                           dest->offset >= func->offset + func->len) {
-                               /* local sibling call */
-                               dest_func = find_symbol_by_offset(dest->sec,
-                                                                 dest->offset);
-                               if (!dest_func)
-                                       continue;
+                       if (dest->func && dest->func->pfunc != insn->func->pfunc) {
 
+                               /* local sibling call */
                                if (recursion == 5) {
-                                       WARN_FUNC("infinite recursion (objtool bug!)",
-                                                 dest->sec, dest->offset);
-                                       return -1;
+                                       /*
+                                        * Infinite recursion: two functions
+                                        * have sibling calls to each other.
+                                        * This is a very rare case.  It means
+                                        * they aren't dead ends.
+                                        */
+                                       return 0;
                                }
 
-                               return __dead_end_function(file, dest_func,
+                               return __dead_end_function(file, dest->func,
                                                           recursion + 1);
                        }
                }
@@ -422,7 +444,7 @@ static void add_ignores(struct objtool_file *file)
                        if (!ignore_func(file, func))
                                continue;
 
-                       func_for_each_insn(file, func, insn)
+                       func_for_each_insn_all(file, func, insn)
                                insn->ignore = true;
                }
        }
@@ -782,30 +804,35 @@ out:
        return ret;
 }
 
-static int add_switch_table(struct objtool_file *file, struct symbol *func,
-                           struct instruction *insn, struct rela *table,
-                           struct rela *next_table)
+static int add_switch_table(struct objtool_file *file, struct instruction *insn,
+                           struct rela *table, struct rela *next_table)
 {
        struct rela *rela = table;
        struct instruction *alt_insn;
        struct alternative *alt;
+       struct symbol *pfunc = insn->func->pfunc;
+       unsigned int prev_offset = 0;
 
        list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) {
                if (rela == next_table)
                        break;
 
-               if (rela->sym->sec != insn->sec ||
-                   rela->addend <= func->offset ||
-                   rela->addend >= func->offset + func->len)
+               /* Make sure the switch table entries are consecutive: */
+               if (prev_offset && rela->offset != prev_offset + 8)
                        break;
 
-               alt_insn = find_insn(file, insn->sec, rela->addend);
-               if (!alt_insn) {
-                       WARN("%s: can't find instruction at %s+0x%x",
-                            file->rodata->rela->name, insn->sec->name,
-                            rela->addend);
-                       return -1;
-               }
+               /* Detect function pointers from contiguous objects: */
+               if (rela->sym->sec == pfunc->sec &&
+                   rela->addend == pfunc->offset)
+                       break;
+
+               alt_insn = find_insn(file, rela->sym->sec, rela->addend);
+               if (!alt_insn)
+                       break;
+
+               /* Make sure the jmp dest is in the function or subfunction: */
+               if (alt_insn->func->pfunc != pfunc)
+                       break;
 
                alt = malloc(sizeof(*alt));
                if (!alt) {
@@ -815,6 +842,13 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func,
 
                alt->insn = alt_insn;
                list_add_tail(&alt->list, &insn->alts);
+               prev_offset = rela->offset;
+       }
+
+       if (!prev_offset) {
+               WARN_FUNC("can't find switch jump table",
+                         insn->sec, insn->offset);
+               return -1;
        }
 
        return 0;
@@ -869,40 +903,21 @@ static struct rela *find_switch_table(struct objtool_file *file,
 {
        struct rela *text_rela, *rodata_rela;
        struct instruction *orig_insn = insn;
+       unsigned long table_offset;
 
-       text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
-       if (text_rela && text_rela->sym == file->rodata->sym) {
-               /* case 1 */
-               rodata_rela = find_rela_by_dest(file->rodata,
-                                               text_rela->addend);
-               if (rodata_rela)
-                       return rodata_rela;
-
-               /* case 2 */
-               rodata_rela = find_rela_by_dest(file->rodata,
-                                               text_rela->addend + 4);
-               if (!rodata_rela)
-                       return NULL;
-
-               file->ignore_unreachables = true;
-               return rodata_rela;
-       }
-
-       /* case 3 */
        /*
         * Backward search using the @first_jump_src links, these help avoid
         * much of the 'in between' code. Which avoids us getting confused by
         * it.
         */
-       for (insn = list_prev_entry(insn, list);
-
+       for (;
             &insn->list != &file->insn_list &&
             insn->sec == func->sec &&
             insn->offset >= func->offset;
 
             insn = insn->first_jump_src ?: list_prev_entry(insn, list)) {
 
-               if (insn->type == INSN_JUMP_DYNAMIC)
+               if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
                        break;
 
                /* allow small jumps within the range */
@@ -918,18 +933,29 @@ static struct rela *find_switch_table(struct objtool_file *file,
                if (!text_rela || text_rela->sym != file->rodata->sym)
                        continue;
 
+               table_offset = text_rela->addend;
+               if (text_rela->type == R_X86_64_PC32)
+                       table_offset += 4;
+
                /*
                 * Make sure the .rodata address isn't associated with a
                 * symbol.  gcc jump tables are anonymous data.
                 */
-               if (find_symbol_containing(file->rodata, text_rela->addend))
+               if (find_symbol_containing(file->rodata, table_offset))
                        continue;
 
-               rodata_rela = find_rela_by_dest(file->rodata, text_rela->addend);
-               if (!rodata_rela)
-                       continue;
+               rodata_rela = find_rela_by_dest(file->rodata, table_offset);
+               if (rodata_rela) {
+                       /*
+                        * Use of RIP-relative switch jumps is quite rare, and
+                        * indicates a rare GCC quirk/bug which can leave dead
+                        * code behind.
+                        */
+                       if (text_rela->type == R_X86_64_PC32)
+                               file->ignore_unreachables = true;
 
-               return rodata_rela;
+                       return rodata_rela;
+               }
        }
 
        return NULL;
@@ -943,7 +969,7 @@ static int add_func_switch_tables(struct objtool_file *file,
        struct rela *rela, *prev_rela = NULL;
        int ret;
 
-       func_for_each_insn(file, func, insn) {
+       func_for_each_insn_all(file, func, insn) {
                if (!last)
                        last = insn;
 
@@ -974,8 +1000,7 @@ static int add_func_switch_tables(struct objtool_file *file,
                 * the beginning of another switch table in the same function.
                 */
                if (prev_jump) {
-                       ret = add_switch_table(file, func, prev_jump, prev_rela,
-                                              rela);
+                       ret = add_switch_table(file, prev_jump, prev_rela, rela);
                        if (ret)
                                return ret;
                }
@@ -985,7 +1010,7 @@ static int add_func_switch_tables(struct objtool_file *file,
        }
 
        if (prev_jump) {
-               ret = add_switch_table(file, func, prev_jump, prev_rela, NULL);
+               ret = add_switch_table(file, prev_jump, prev_rela, NULL);
                if (ret)
                        return ret;
        }
@@ -1749,15 +1774,13 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
        while (1) {
                next_insn = next_insn_same_sec(file, insn);
 
-
-               if (file->c_file && func && insn->func && func != insn->func) {
+               if (file->c_file && func && insn->func && func != insn->func->pfunc) {
                        WARN("%s() falls through to next function %s()",
                             func->name, insn->func->name);
                        return 1;
                }
 
-               if (insn->func)
-                       func = insn->func;
+               func = insn->func ? insn->func->pfunc : NULL;
 
                if (func && insn->ignore) {
                        WARN_FUNC("BUG: why am I validating an ignored function?",
@@ -1778,7 +1801,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
 
                                i = insn;
                                save_insn = NULL;
-                               func_for_each_insn_continue_reverse(file, func, i) {
+                               func_for_each_insn_continue_reverse(file, insn->func, i) {
                                        if (i->save) {
                                                save_insn = i;
                                                break;
@@ -1865,7 +1888,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
                case INSN_JUMP_UNCONDITIONAL:
                        if (insn->jump_dest &&
                            (!func || !insn->jump_dest->func ||
-                            func == insn->jump_dest->func)) {
+                            insn->jump_dest->func->pfunc == func)) {
                                ret = validate_branch(file, insn->jump_dest,
                                                      state);
                                if (ret)
@@ -2060,7 +2083,7 @@ static int validate_functions(struct objtool_file *file)
 
        for_each_sec(file, sec) {
                list_for_each_entry(func, &sec->symbol_list, list) {
-                       if (func->type != STT_FUNC)
+                       if (func->type != STT_FUNC || func->pfunc != func)
                                continue;
 
                        insn = find_insn(file, sec, func->offset);
index c1c3386..4e60e10 100644 (file)
@@ -79,6 +79,19 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
        return NULL;
 }
 
+struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
+{
+       struct section *sec;
+       struct symbol *sym;
+
+       list_for_each_entry(sec, &elf->sections, list)
+               list_for_each_entry(sym, &sec->symbol_list, list)
+                       if (!strcmp(sym->name, name))
+                               return sym;
+
+       return NULL;
+}
+
 struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
 {
        struct symbol *sym;
@@ -203,10 +216,11 @@ static int read_sections(struct elf *elf)
 
 static int read_symbols(struct elf *elf)
 {
-       struct section *symtab;
-       struct symbol *sym;
+       struct section *symtab, *sec;
+       struct symbol *sym, *pfunc;
        struct list_head *entry, *tmp;
        int symbols_nr, i;
+       char *coldstr;
 
        symtab = find_section_by_name(elf, ".symtab");
        if (!symtab) {
@@ -281,6 +295,30 @@ static int read_symbols(struct elf *elf)
                hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
        }
 
+       /* 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) {
+                       if (sym->type != STT_FUNC)
+                               continue;
+                       sym->pfunc = sym->cfunc = sym;
+                       coldstr = strstr(sym->name, ".cold.");
+                       if (coldstr) {
+                               coldstr[0] = '\0';
+                               pfunc = find_symbol_by_name(elf, sym->name);
+                               coldstr[0] = '.';
+
+                               if (!pfunc) {
+                                       WARN("%s(): can't find parent function",
+                                            sym->name);
+                                       goto err;
+                               }
+
+                               sym->pfunc = pfunc;
+                               pfunc->cfunc = sym;
+                       }
+               }
+       }
+
        return 0;
 
 err:
index d86e2ff..de5cd2d 100644 (file)
@@ -61,6 +61,7 @@ struct symbol {
        unsigned char bind, type;
        unsigned long offset;
        unsigned int len;
+       struct symbol *pfunc, *cfunc;
 };
 
 struct rela {
@@ -86,6 +87,7 @@ struct elf {
 struct elf *elf_open(const char *name, int flags);
 struct section *find_section_by_name(struct elf *elf, const char *name);
 struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
+struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
 struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
 struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
 struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
index 944070e..63eb490 100644 (file)
@@ -175,7 +175,7 @@ static const struct option options[] = {
        OPT_UINTEGER('s', "nr_secs"     , &p0.nr_secs,          "max number of seconds to run (default: 5 secs)"),
        OPT_UINTEGER('u', "usleep"      , &p0.sleep_usecs,      "usecs to sleep per loop iteration"),
 
-       OPT_BOOLEAN('R', "data_reads"   , &p0.data_reads,       "access the data via writes (can be mixed with -W)"),
+       OPT_BOOLEAN('R', "data_reads"   , &p0.data_reads,       "access the data via reads (can be mixed with -W)"),
        OPT_BOOLEAN('W', "data_writes"  , &p0.data_writes,      "access the data via writes (can be mixed with -R)"),
        OPT_BOOLEAN('B', "data_backwards", &p0.data_backwards,  "access the data backwards as well"),
        OPT_BOOLEAN('Z', "data_zero_memset", &p0.data_zero_memset,"access the data via glibc bzero only"),
index 93656f2..7e3cce3 100644 (file)
@@ -29,7 +29,6 @@ GenuineIntel-6-4D,v13,silvermont,core
 GenuineIntel-6-4C,v13,silvermont,core
 GenuineIntel-6-2A,v15,sandybridge,core
 GenuineIntel-6-2C,v2,westmereep-dp,core
-GenuineIntel-6-2C,v2,westmereep-dp,core
 GenuineIntel-6-25,v2,westmereep-sp,core
 GenuineIntel-6-2F,v2,westmereex,core
 GenuineIntel-6-55,v1,skylakex,core
index 016882d..ee86473 100755 (executable)
@@ -16,7 +16,7 @@ nm -g $libc 2>/dev/null | fgrep -q inet_pton || exit 254
 trace_libc_inet_pton_backtrace() {
        idx=0
        expected[0]="ping[][0-9 \.:]+probe_libc:inet_pton: \([[:xdigit:]]+\)"
-       expected[1]=".*inet_pton[[:space:]]\($libc\)$"
+       expected[1]=".*inet_pton[[:space:]]\($libc|inlined\)$"
        case "$(uname -m)" in
        s390x)
                eventattr='call-graph=dwarf,max-stack=4'
index 536ee14..5d74a30 100644 (file)
@@ -1263,6 +1263,9 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start
                                max_percent = sample->percent;
                }
 
+               if (al->samples_nr > nr_percent)
+                       nr_percent = al->samples_nr;
+
                if (max_percent < min_pcnt)
                        return -1;
 
index 40020b1..bf16dc9 100644 (file)
@@ -239,6 +239,7 @@ static void cs_etm__free(struct perf_session *session)
        for (i = 0; i < aux->num_cpu; i++)
                zfree(&aux->metadata[i]);
 
+       thread__zput(aux->unknown_thread);
        zfree(&aux->metadata);
        zfree(&aux);
 }
@@ -612,8 +613,8 @@ cs_etm__get_trace(struct cs_etm_buffer *buff, struct cs_etm_queue *etmq)
        return buff->len;
 }
 
-static void  cs_etm__set_pid_tid_cpu(struct cs_etm_auxtrace *etm,
-                                    struct auxtrace_queue *queue)
+static void cs_etm__set_pid_tid_cpu(struct cs_etm_auxtrace *etm,
+                                   struct auxtrace_queue *queue)
 {
        struct cs_etm_queue *etmq = queue->priv;
 
@@ -1357,6 +1358,23 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
        etm->auxtrace.free = cs_etm__free;
        session->auxtrace = &etm->auxtrace;
 
+       etm->unknown_thread = thread__new(999999999, 999999999);
+       if (!etm->unknown_thread)
+               goto err_free_queues;
+
+       /*
+        * Initialize list node so that at thread__zput() we can avoid
+        * segmentation fault at list_del_init().
+        */
+       INIT_LIST_HEAD(&etm->unknown_thread->node);
+
+       err = thread__set_comm(etm->unknown_thread, "unknown", 0);
+       if (err)
+               goto err_delete_thread;
+
+       if (thread__init_map_groups(etm->unknown_thread, etm->machine))
+               goto err_delete_thread;
+
        if (dump_trace) {
                cs_etm__print_auxtrace_info(auxtrace_info->priv, num_cpu);
                return 0;
@@ -1371,16 +1389,18 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
 
        err = cs_etm__synth_events(etm, session);
        if (err)
-               goto err_free_queues;
+               goto err_delete_thread;
 
        err = auxtrace_queues__process_index(&etm->queues, session);
        if (err)
-               goto err_free_queues;
+               goto err_delete_thread;
 
        etm->data_queued = etm->queues.populated;
 
        return 0;
 
+err_delete_thread:
+       thread__zput(etm->unknown_thread);
 err_free_queues:
        auxtrace_queues__free(&etm->queues);
        session->auxtrace = NULL;
index 2fb0272..b8b8a95 100644 (file)
@@ -1715,7 +1715,7 @@ int parse_events(struct perf_evlist *evlist, const char *str,
                struct perf_evsel *last;
 
                if (list_empty(&parse_state.list)) {
-                       WARN_ONCE(true, "WARNING: event parser found nothing");
+                       WARN_ONCE(true, "WARNING: event parser found nothing\n");
                        return -1;
                }
 
index d14464c..7afeb80 100644 (file)
@@ -224,15 +224,15 @@ event_def: event_pmu |
           event_bpf_file
 
 event_pmu:
-PE_NAME '/' event_config '/'
+PE_NAME opt_event_config
 {
        struct list_head *list, *orig_terms, *terms;
 
-       if (parse_events_copy_term_list($3, &orig_terms))
+       if (parse_events_copy_term_list($2, &orig_terms))
                YYABORT;
 
        ALLOC_LIST(list);
-       if (parse_events_add_pmu(_parse_state, list, $1, $3, false)) {
+       if (parse_events_add_pmu(_parse_state, list, $1, $2, false)) {
                struct perf_pmu *pmu = NULL;
                int ok = 0;
                char *pattern;
@@ -262,7 +262,7 @@ PE_NAME '/' event_config '/'
                if (!ok)
                        YYABORT;
        }
-       parse_events_terms__delete($3);
+       parse_events_terms__delete($2);
        parse_events_terms__delete(orig_terms);
        $$ = list;
 }
index fa7ee36..db66f8a 100644 (file)
@@ -17,7 +17,7 @@ ifeq ($(BUILD), 32)
        LDFLAGS += -m32
 endif
 
-targets: mapshift $(TARGETS)
+targets: generated/map-shift.h $(TARGETS)
 
 main:  $(OFILES)
 
@@ -42,9 +42,7 @@ radix-tree.c: ../../../lib/radix-tree.c
 idr.c: ../../../lib/idr.c
        sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@
 
-.PHONY: mapshift
-
-mapshift:
+generated/map-shift.h:
        @if ! grep -qws $(SHIFT) generated/map-shift.h; then            \
                echo "#define RADIX_TREE_MAP_SHIFT $(SHIFT)" >          \
                                generated/map-shift.h;                  \
index 59245b3..7bf4056 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/radix-tree.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
+#include <pthread.h>
 
 #include "test.h"
 
@@ -624,6 +625,67 @@ static void multiorder_account(void)
        item_kill_tree(&tree);
 }
 
+bool stop_iteration = false;
+
+static void *creator_func(void *ptr)
+{
+       /* 'order' is set up to ensure we have sibling entries */
+       unsigned int order = RADIX_TREE_MAP_SHIFT - 1;
+       struct radix_tree_root *tree = ptr;
+       int i;
+
+       for (i = 0; i < 10000; i++) {
+               item_insert_order(tree, 0, order);
+               item_delete_rcu(tree, 0);
+       }
+
+       stop_iteration = true;
+       return NULL;
+}
+
+static void *iterator_func(void *ptr)
+{
+       struct radix_tree_root *tree = ptr;
+       struct radix_tree_iter iter;
+       struct item *item;
+       void **slot;
+
+       while (!stop_iteration) {
+               rcu_read_lock();
+               radix_tree_for_each_slot(slot, tree, &iter, 0) {
+                       item = radix_tree_deref_slot(slot);
+
+                       if (!item)
+                               continue;
+                       if (radix_tree_deref_retry(item)) {
+                               slot = radix_tree_iter_retry(&iter);
+                               continue;
+                       }
+
+                       item_sanity(item, iter.index);
+               }
+               rcu_read_unlock();
+       }
+       return NULL;
+}
+
+static void multiorder_iteration_race(void)
+{
+       const int num_threads = sysconf(_SC_NPROCESSORS_ONLN);
+       pthread_t worker_thread[num_threads];
+       RADIX_TREE(tree, GFP_KERNEL);
+       int i;
+
+       pthread_create(&worker_thread[0], NULL, &creator_func, &tree);
+       for (i = 1; i < num_threads; i++)
+               pthread_create(&worker_thread[i], NULL, &iterator_func, &tree);
+
+       for (i = 0; i < num_threads; i++)
+               pthread_join(worker_thread[i], NULL);
+
+       item_kill_tree(&tree);
+}
+
 void multiorder_checks(void)
 {
        int i;
@@ -644,6 +706,7 @@ void multiorder_checks(void)
        multiorder_join();
        multiorder_split();
        multiorder_account();
+       multiorder_iteration_race();
 
        radix_tree_cpu_dead(0);
 }
index 5978ab1..def6015 100644 (file)
@@ -75,6 +75,25 @@ int item_delete(struct radix_tree_root *root, unsigned long index)
        return 0;
 }
 
+static void item_free_rcu(struct rcu_head *head)
+{
+       struct item *item = container_of(head, struct item, rcu_head);
+
+       free(item);
+}
+
+int item_delete_rcu(struct radix_tree_root *root, unsigned long index)
+{
+       struct item *item = radix_tree_delete(root, index);
+
+       if (item) {
+               item_sanity(item, index);
+               call_rcu(&item->rcu_head, item_free_rcu);
+               return 1;
+       }
+       return 0;
+}
+
 void item_check_present(struct radix_tree_root *root, unsigned long index)
 {
        struct item *item;
index d9c031d..31f1d9b 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/rcupdate.h>
 
 struct item {
+       struct rcu_head rcu_head;
        unsigned long index;
        unsigned int order;
 };
@@ -12,9 +13,11 @@ struct item {
 struct item *item_create(unsigned long index, unsigned int order);
 int __item_insert(struct radix_tree_root *root, struct item *item);
 int item_insert(struct radix_tree_root *root, unsigned long index);
+void item_sanity(struct item *item, unsigned long index);
 int item_insert_order(struct radix_tree_root *root, unsigned long index,
                        unsigned order);
 int item_delete(struct radix_tree_root *root, unsigned long index);
+int item_delete_rcu(struct radix_tree_root *root, unsigned long index);
 struct item *item_lookup(struct radix_tree_root *root, unsigned long index);
 
 void item_check_present(struct radix_tree_root *root, unsigned long index);
index 3e3b3ce..adc8e54 100644 (file)
@@ -16,3 +16,4 @@ test_sock
 test_sock_addr
 urandom_read
 test_btf
+test_sockmap
index 9d76218..1eb0fa2 100644 (file)
@@ -10,7 +10,7 @@ ifneq ($(wildcard $(GENHDR)),)
   GENFLAGS := -DHAVE_GENHDR
 endif
 
-CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include
+CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include
 LDLIBS += -lcap -lelf -lrt -lpthread
 
 TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read
@@ -19,7 +19,7 @@ all: $(TEST_CUSTOM_PROGS)
 $(TEST_CUSTOM_PROGS): urandom_read
 
 urandom_read: urandom_read.c
-       $(CC) -o $(TEST_CUSTOM_PROGS) -static $<
+       $(CC) -o $(TEST_CUSTOM_PROGS) -static $< -Wl,--build-id
 
 # Order correspond to 'make run_tests' order
 TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
@@ -33,7 +33,7 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test
        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 \
-       test_get_stack_rawtp.o
+       test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o
 
 # Order correspond to 'make run_tests' order
 TEST_PROGS := test_kmod.sh \
@@ -90,9 +90,9 @@ CLANG_FLAGS = -I. -I./include/uapi -I../../../include/uapi \
 $(OUTPUT)/test_l4lb_noinline.o: CLANG_FLAGS += -fno-inline
 $(OUTPUT)/test_xdp_noinline.o: CLANG_FLAGS += -fno-inline
 
-BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help |& grep dwarfris)
-BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help |& grep BTF)
-BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --version |& grep LLVM)
+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) --version 2>&1 | grep LLVM)
 
 ifneq ($(BTF_LLC_PROBE),)
 ifneq ($(BTF_PAHOLE_PROBE),)
index 265f8e0..8f143df 100644 (file)
@@ -75,9 +75,14 @@ static int (*bpf_sock_ops_cb_flags_set)(void *ctx, int flags) =
        (void *) BPF_FUNC_sock_ops_cb_flags_set;
 static int (*bpf_sk_redirect_map)(void *ctx, void *map, int key, int flags) =
        (void *) BPF_FUNC_sk_redirect_map;
+static int (*bpf_sk_redirect_hash)(void *ctx, void *map, void *key, int flags) =
+       (void *) BPF_FUNC_sk_redirect_hash;
 static int (*bpf_sock_map_update)(void *map, void *key, void *value,
                                  unsigned long long flags) =
        (void *) BPF_FUNC_sock_map_update;
+static int (*bpf_sock_hash_update)(void *map, void *key, void *value,
+                                  unsigned long long flags) =
+       (void *) BPF_FUNC_sock_hash_update;
 static int (*bpf_perf_event_read_value)(void *map, unsigned long long flags,
                                        void *buf, unsigned int buf_size) =
        (void *) BPF_FUNC_perf_event_read_value;
@@ -88,6 +93,9 @@ static int (*bpf_override_return)(void *ctx, unsigned long rc) =
        (void *) BPF_FUNC_override_return;
 static int (*bpf_msg_redirect_map)(void *ctx, void *map, int key, int flags) =
        (void *) BPF_FUNC_msg_redirect_map;
+static int (*bpf_msg_redirect_hash)(void *ctx,
+                                   void *map, void *key, int flags) =
+       (void *) BPF_FUNC_msg_redirect_hash;
 static int (*bpf_msg_apply_bytes)(void *ctx, int len) =
        (void *) BPF_FUNC_msg_apply_bytes;
 static int (*bpf_msg_cork_bytes)(void *ctx, int len) =
@@ -103,6 +111,9 @@ static int (*bpf_skb_get_xfrm_state)(void *ctx, int index, void *state,
        (void *) BPF_FUNC_skb_get_xfrm_state;
 static int (*bpf_get_stack)(void *ctx, void *buf, int size, int flags) =
        (void *) BPF_FUNC_get_stack;
+static int (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params,
+                            int plen, __u32 flags) =
+       (void *) BPF_FUNC_fib_lookup;
 
 /* llvm builtin functions that eBPF C program may use to
  * emit BPF_LD_ABS and BPF_LD_IND instructions
diff --git a/tools/testing/selftests/bpf/bpf_rand.h b/tools/testing/selftests/bpf/bpf_rand.h
new file mode 100644 (file)
index 0000000..59bf3e1
--- /dev/null
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BPF_RAND__
+#define __BPF_RAND__
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+
+static inline uint64_t bpf_rand_mask(uint64_t mask)
+{
+       return (((uint64_t)(uint32_t)rand()) |
+               ((uint64_t)(uint32_t)rand() << 32)) & mask;
+}
+
+#define bpf_rand_ux(x, m)                      \
+static inline uint64_t bpf_rand_u##x(int shift)        \
+{                                              \
+       return bpf_rand_mask((m)) << shift;     \
+}
+
+bpf_rand_ux( 8,               0xffULL)
+bpf_rand_ux(16,             0xffffULL)
+bpf_rand_ux(24,           0xffffffULL)
+bpf_rand_ux(32,         0xffffffffULL)
+bpf_rand_ux(40,       0xffffffffffULL)
+bpf_rand_ux(48,     0xffffffffffffULL)
+bpf_rand_ux(56,   0xffffffffffffffULL)
+bpf_rand_ux(64, 0xffffffffffffffffULL)
+
+static inline void bpf_semi_rand_init(void)
+{
+       srand(time(NULL));
+}
+
+static inline uint64_t bpf_semi_rand_get(void)
+{
+       switch (rand() % 39) {
+       case  0: return 0x000000ff00000000ULL | bpf_rand_u8(0);
+       case  1: return 0xffffffff00000000ULL | bpf_rand_u16(0);
+       case  2: return 0x00000000ffff0000ULL | bpf_rand_u16(0);
+       case  3: return 0x8000000000000000ULL | bpf_rand_u32(0);
+       case  4: return 0x00000000f0000000ULL | bpf_rand_u32(0);
+       case  5: return 0x0000000100000000ULL | bpf_rand_u24(0);
+       case  6: return 0x800ff00000000000ULL | bpf_rand_u32(0);
+       case  7: return 0x7fffffff00000000ULL | bpf_rand_u32(0);
+       case  8: return 0xffffffffffffff00ULL ^ bpf_rand_u32(24);
+       case  9: return 0xffffffffffffff00ULL | bpf_rand_u8(0);
+       case 10: return 0x0000000010000000ULL | bpf_rand_u32(0);
+       case 11: return 0xf000000000000000ULL | bpf_rand_u8(0);
+       case 12: return 0x0000f00000000000ULL | bpf_rand_u8(8);
+       case 13: return 0x000000000f000000ULL | bpf_rand_u8(16);
+       case 14: return 0x0000000000000f00ULL | bpf_rand_u8(32);
+       case 15: return 0x00fff00000000f00ULL | bpf_rand_u8(48);
+       case 16: return 0x00007fffffffffffULL ^ bpf_rand_u32(1);
+       case 17: return 0xffff800000000000ULL | bpf_rand_u8(4);
+       case 18: return 0xffff800000000000ULL | bpf_rand_u8(20);
+       case 19: return (0xffffffc000000000ULL + 0x80000ULL) | bpf_rand_u32(0);
+       case 20: return (0xffffffc000000000ULL - 0x04000000ULL) | bpf_rand_u32(0);
+       case 21: return 0x0000000000000000ULL | bpf_rand_u8(55) | bpf_rand_u32(20);
+       case 22: return 0xffffffffffffffffULL ^ bpf_rand_u8(3) ^ bpf_rand_u32(40);
+       case 23: return 0x0000000000000000ULL | bpf_rand_u8(bpf_rand_u8(0) % 64);
+       case 24: return 0x0000000000000000ULL | bpf_rand_u16(bpf_rand_u8(0) % 64);
+       case 25: return 0xffffffffffffffffULL ^ bpf_rand_u8(bpf_rand_u8(0) % 64);
+       case 26: return 0xffffffffffffffffULL ^ bpf_rand_u40(bpf_rand_u8(0) % 64);
+       case 27: return 0x0000800000000000ULL;
+       case 28: return 0x8000000000000000ULL;
+       case 29: return 0x0000000000000000ULL;
+       case 30: return 0xffffffffffffffffULL;
+       case 31: return bpf_rand_u16(bpf_rand_u8(0) % 64);
+       case 32: return bpf_rand_u24(bpf_rand_u8(0) % 64);
+       case 33: return bpf_rand_u32(bpf_rand_u8(0) % 64);
+       case 34: return bpf_rand_u40(bpf_rand_u8(0) % 64);
+       case 35: return bpf_rand_u48(bpf_rand_u8(0) % 64);
+       case 36: return bpf_rand_u56(bpf_rand_u8(0) % 64);
+       case 37: return bpf_rand_u64(bpf_rand_u8(0) % 64);
+       default: return bpf_rand_u64(0);
+       }
+}
+
+#endif /* __BPF_RAND__ */
index 7b39b1f..c8bceae 100644 (file)
 
 #include "bpf_rlimit.h"
 
+static uint32_t pass_cnt;
+static uint32_t error_cnt;
+static uint32_t skip_cnt;
+
+#define CHECK(condition, format...) ({                                 \
+       int __ret = !!(condition);                                      \
+       if (__ret) {                                                    \
+               fprintf(stderr, "%s:%d:FAIL ", __func__, __LINE__);     \
+               fprintf(stderr, format);                                \
+       }                                                               \
+       __ret;                                                          \
+})
+
+static int count_result(int err)
+{
+       if (err)
+               error_cnt++;
+       else
+               pass_cnt++;
+
+       fprintf(stderr, "\n");
+       return err;
+}
+
 #define min(a, b) ((a) < (b) ? (a) : (b))
 #define __printf(a, b) __attribute__((format(printf, a, b)))
 
@@ -894,17 +918,13 @@ static void *btf_raw_create(const struct btf_header *hdr,
        void *raw_btf;
 
        type_sec_size = get_type_sec_size(raw_types);
-       if (type_sec_size < 0) {
-               fprintf(stderr, "Cannot get nr_raw_types\n");
+       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 (!raw_btf) {
-               fprintf(stderr, "Cannot allocate memory for raw_btf\n");
+       if (CHECK(!raw_btf, "Cannot allocate memory for raw_btf"))
                return NULL;
-       }
 
        /* Copy header */
        memcpy(raw_btf, hdr, sizeof(*hdr));
@@ -915,8 +935,7 @@ static void *btf_raw_create(const struct btf_header *hdr,
        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 (!next_str) {
-                               fprintf(stderr, "Error in getting next_str\n");
+                       if (CHECK(!next_str, "Error in getting next_str")) {
                                free(raw_btf);
                                return NULL;
                        }
@@ -973,9 +992,8 @@ static int do_test_raw(unsigned int test_num)
        free(raw_btf);
 
        err = ((btf_fd == -1) != test->btf_load_err);
-       if (err)
-               fprintf(stderr, "btf_load_err:%d btf_fd:%d\n",
-                       test->btf_load_err, btf_fd);
+       CHECK(err, "btf_fd:%d test->btf_load_err:%u",
+             btf_fd, test->btf_load_err);
 
        if (err || btf_fd == -1)
                goto done;
@@ -992,16 +1010,15 @@ static int do_test_raw(unsigned int test_num)
        map_fd = bpf_create_map_xattr(&create_attr);
 
        err = ((map_fd == -1) != test->map_create_err);
-       if (err)
-               fprintf(stderr, "map_create_err:%d map_fd:%d\n",
-                       test->map_create_err, map_fd);
+       CHECK(err, "map_fd:%d test->map_create_err:%u",
+             map_fd, test->map_create_err);
 
 done:
        if (!err)
-               fprintf(stderr, "OK\n");
+               fprintf(stderr, "OK");
 
        if (*btf_log_buf && (err || args.always_log))
-               fprintf(stderr, "%s\n", btf_log_buf);
+               fprintf(stderr, "\n%s", btf_log_buf);
 
        if (btf_fd != -1)
                close(btf_fd);
@@ -1017,10 +1034,10 @@ static int test_raw(void)
        int err = 0;
 
        if (args.raw_test_num)
-               return do_test_raw(args.raw_test_num);
+               return count_result(do_test_raw(args.raw_test_num));
 
        for (i = 1; i <= ARRAY_SIZE(raw_tests); i++)
-               err |= do_test_raw(i);
+               err |= count_result(do_test_raw(i));
 
        return err;
 }
@@ -1030,9 +1047,13 @@ struct btf_get_info_test {
        const char *str_sec;
        __u32 raw_types[MAX_NR_RAW_TYPES];
        __u32 str_sec_size;
-       int info_size_delta;
+       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",
@@ -1043,7 +1064,7 @@ const struct btf_get_info_test get_info_tests[] = {
        },
        .str_sec = "",
        .str_sec_size = sizeof(""),
-       .info_size_delta = 1,
+       .btf_size_delta = 1,
 },
 {
        .descr = "== raw_btf_size-3",
@@ -1054,20 +1075,274 @@ const struct btf_get_info_test get_info_tests[] = {
        },
        .str_sec = "",
        .str_sec_size = sizeof(""),
-       .info_size_delta = -3,
+       .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)
+{
+       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);
+
+       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);
+
+       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_id = 1;
+       create_attr.btf_value_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_id != 1 || map_info.btf_value_id != 2,
+                 "err:%d errno:%d info.id:%u btf_id:%u btf_key_id:%u btf_value_id:%u",
+                 err, errno, info[0].id, map_info.btf_id, map_info.btf_key_id,
+                 map_info.btf_value_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;
-       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_BY_ID test[%u] (%s): ",
+       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,
@@ -1080,8 +1355,7 @@ static int do_test_get_info(unsigned int test_num)
        *btf_log_buf = '\0';
 
        user_btf = malloc(raw_btf_size);
-       if (!user_btf) {
-               fprintf(stderr, "Cannot allocate memory for user_btf\n");
+       if (CHECK(!user_btf, "!user_btf")) {
                err = -1;
                goto done;
        }
@@ -1089,45 +1363,48 @@ static int do_test_get_info(unsigned int test_num)
        btf_fd = bpf_load_btf(raw_btf, raw_btf_size,
                              btf_log_buf, BTF_LOG_BUF_SIZE,
                              args.always_log);
-       if (btf_fd == -1) {
-               fprintf(stderr, "bpf_load_btf:%s(%d)\n",
-                       strerror(errno), errno);
+       if (CHECK(btf_fd == -1, "errno:%d", errno)) {
                err = -1;
                goto done;
        }
 
-       user_btf_size = (int)raw_btf_size + test->info_size_delta;
+       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);
 
-       err = bpf_obj_get_info_by_fd(btf_fd, user_btf, &user_btf_size);
-       if (err || user_btf_size != raw_btf_size ||
-           memcmp(raw_btf, user_btf, expected_nbytes)) {
-               fprintf(stderr,
-                       "err:%d(errno:%d) raw_btf_size:%u user_btf_size:%u expected_nbytes:%u memcmp:%d\n",
-                       err, errno,
-                       raw_btf_size, user_btf_size, expected_nbytes,
-                       memcmp(raw_btf, user_btf, 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 (user_btf[expected_nbytes++] != 0xff) {
-                       fprintf(stderr, "!= 0xff\n");
+               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\n");
+       fprintf(stderr, "OK");
 
 done:
        if (*btf_log_buf && (err || args.always_log))
-               fprintf(stderr, "%s\n", btf_log_buf);
+               fprintf(stderr, "\n%s", btf_log_buf);
 
        free(raw_btf);
        free(user_btf);
@@ -1144,10 +1421,10 @@ static int test_get_info(void)
        int err = 0;
 
        if (args.get_info_test_num)
-               return do_test_get_info(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 |= do_test_get_info(i);
+               err |= count_result(do_test_get_info(i));
 
        return err;
 }
@@ -1175,28 +1452,21 @@ static int file_has_btf_elf(const char *fn)
        Elf *elf;
        int ret;
 
-       if (elf_version(EV_CURRENT) == EV_NONE) {
-               fprintf(stderr, "Failed to init libelf\n");
+       if (CHECK(elf_version(EV_CURRENT) == EV_NONE,
+                 "elf_version(EV_CURRENT) == EV_NONE"))
                return -1;
-       }
 
        elf_fd = open(fn, O_RDONLY);
-       if (elf_fd == -1) {
-               fprintf(stderr, "Cannot open file %s: %s(%d)\n",
-                       fn, strerror(errno), errno);
+       if (CHECK(elf_fd == -1, "open(%s): errno:%d", fn, errno))
                return -1;
-       }
 
        elf = elf_begin(elf_fd, ELF_C_READ, NULL);
-       if (!elf) {
-               fprintf(stderr, "Failed to read ELF from %s. %s\n", fn,
-                       elf_errmsg(elf_errno()));
+       if (CHECK(!elf, "elf_begin(%s): %s", fn, elf_errmsg(elf_errno()))) {
                ret = -1;
                goto done;
        }
 
-       if (!gelf_getehdr(elf, &ehdr)) {
-               fprintf(stderr, "Failed to get EHDR from %s\n", fn);
+       if (CHECK(!gelf_getehdr(elf, &ehdr), "!gelf_getehdr(%s)", fn)) {
                ret = -1;
                goto done;
        }
@@ -1205,9 +1475,8 @@ static int file_has_btf_elf(const char *fn)
                const char *sh_name;
                GElf_Shdr sh;
 
-               if (gelf_getshdr(scn, &sh) != &sh) {
-                       fprintf(stderr,
-                               "Failed to get section header from %s\n", fn);
+               if (CHECK(gelf_getshdr(scn, &sh) != &sh,
+                         "file:%s gelf_getshdr != &sh", fn)) {
                        ret = -1;
                        goto done;
                }
@@ -1243,53 +1512,44 @@ static int do_test_file(unsigned int test_num)
                return err;
 
        if (err == 0) {
-               fprintf(stderr, "SKIP. No ELF %s found\n", BTF_ELF_SEC);
+               fprintf(stderr, "SKIP. No ELF %s found", BTF_ELF_SEC);
+               skip_cnt++;
                return 0;
        }
 
        obj = bpf_object__open(test->file);
-       if (IS_ERR(obj))
+       if (CHECK(IS_ERR(obj), "obj: %ld", PTR_ERR(obj)))
                return PTR_ERR(obj);
 
        err = bpf_object__btf_fd(obj);
-       if (err == -1) {
-               fprintf(stderr, "bpf_object__btf_fd: -1\n");
+       if (CHECK(err == -1, "bpf_object__btf_fd: -1"))
                goto done;
-       }
 
        prog = bpf_program__next(NULL, obj);
-       if (!prog) {
-               fprintf(stderr, "Cannot find bpf_prog\n");
+       if (CHECK(!prog, "Cannot find bpf_prog")) {
                err = -1;
                goto done;
        }
 
        bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT);
        err = bpf_object__load(obj);
-       if (err < 0) {
-               fprintf(stderr, "bpf_object__load: %d\n", err);
+       if (CHECK(err < 0, "bpf_object__load: %d", err))
                goto done;
-       }
 
        map = bpf_object__find_map_by_name(obj, "btf_map");
-       if (!map) {
-               fprintf(stderr, "btf_map not found\n");
+       if (CHECK(!map, "btf_map not found")) {
                err = -1;
                goto done;
        }
 
        err = (bpf_map__btf_key_id(map) == 0 || bpf_map__btf_value_id(map) == 0)
                != test->btf_kv_notfound;
-       if (err) {
-               fprintf(stderr,
-                       "btf_kv_notfound:%u btf_key_id:%u btf_value_id:%u\n",
-                       test->btf_kv_notfound,
-                       bpf_map__btf_key_id(map),
-                       bpf_map__btf_value_id(map));
+       if (CHECK(err, "btf_key_id:%u btf_value_id:%u test->btf_kv_notfound:%u",
+                 bpf_map__btf_key_id(map), bpf_map__btf_value_id(map),
+                 test->btf_kv_notfound))
                goto done;
-       }
 
-       fprintf(stderr, "OK\n");
+       fprintf(stderr, "OK");
 
 done:
        bpf_object__close(obj);
@@ -1302,10 +1562,10 @@ static int test_file(void)
        int err = 0;
 
        if (args.file_test_num)
-               return do_test_file(args.file_test_num);
+               return count_result(do_test_file(args.file_test_num));
 
        for (i = 1; i <= ARRAY_SIZE(file_tests); i++)
-               err |= do_test_file(i);
+               err |= count_result(do_test_file(i));
 
        return err;
 }
@@ -1425,7 +1685,7 @@ static int test_pprint(void)
        unsigned int key;
        uint8_t *raw_btf;
        ssize_t nread;
-       int err;
+       int err, ret;
 
        fprintf(stderr, "%s......", test->descr);
        raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
@@ -1441,10 +1701,8 @@ static int test_pprint(void)
                              args.always_log);
        free(raw_btf);
 
-       if (btf_fd == -1) {
+       if (CHECK(btf_fd == -1, "errno:%d", errno)) {
                err = -1;
-               fprintf(stderr, "bpf_load_btf: %s(%d)\n",
-                       strerror(errno), errno);
                goto done;
        }
 
@@ -1458,26 +1716,23 @@ static int test_pprint(void)
        create_attr.btf_value_id = test->value_id;
 
        map_fd = bpf_create_map_xattr(&create_attr);
-       if (map_fd == -1) {
+       if (CHECK(map_fd == -1, "errno:%d", errno)) {
                err = -1;
-               fprintf(stderr, "bpf_creat_map_btf: %s(%d)\n",
-                       strerror(errno), errno);
                goto done;
        }
 
-       if (snprintf(pin_path, sizeof(pin_path), "%s/%s",
-                    "/sys/fs/bpf", test->map_name) == sizeof(pin_path)) {
+       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)) {
                err = -1;
-               fprintf(stderr, "pin_path is too long\n");
                goto done;
        }
 
        err = bpf_obj_pin(map_fd, pin_path);
-       if (err) {
-               fprintf(stderr, "Cannot pin to %s. %s(%d).\n", pin_path,
-                       strerror(errno), errno);
+       if (CHECK(err, "bpf_obj_pin(%s): errno:%d.", pin_path, errno))
                goto done;
-       }
 
        for (key = 0; key < test->max_entries; key++) {
                set_pprint_mapv(&mapv, key);
@@ -1485,10 +1740,8 @@ static int test_pprint(void)
        }
 
        pin_file = fopen(pin_path, "r");
-       if (!pin_file) {
+       if (CHECK(!pin_file, "fopen(%s): errno:%d", pin_path, errno)) {
                err = -1;
-               fprintf(stderr, "fopen(%s): %s(%d)\n", pin_path,
-                       strerror(errno), errno);
                goto done;
        }
 
@@ -1497,9 +1750,8 @@ static int test_pprint(void)
               *line == '#')
                ;
 
-       if (nread <= 0) {
+       if (CHECK(nread <= 0, "Unexpected EOF")) {
                err = -1;
-               fprintf(stderr, "Unexpected EOF\n");
                goto done;
        }
 
@@ -1518,9 +1770,9 @@ static int test_pprint(void)
                                          mapv.ui8a[4], mapv.ui8a[5], mapv.ui8a[6], mapv.ui8a[7],
                                          pprint_enum_str[mapv.aenum]);
 
-               if (nexpected_line == sizeof(expected_line)) {
+               if (CHECK(nexpected_line == sizeof(expected_line),
+                         "expected_line is too long")) {
                        err = -1;
-                       fprintf(stderr, "expected_line is too long\n");
                        goto done;
                }
 
@@ -1535,15 +1787,15 @@ static int test_pprint(void)
                nread = getline(&line, &line_len, pin_file);
        } while (++key < test->max_entries && nread > 0);
 
-       if (key < test->max_entries) {
+       if (CHECK(key < test->max_entries,
+                 "Unexpected EOF. key:%u test->max_entries:%u",
+                 key, test->max_entries)) {
                err = -1;
-               fprintf(stderr, "Unexpected EOF\n");
                goto done;
        }
 
-       if (nread > 0) {
+       if (CHECK(nread > 0, "Unexpected extra pprint output: %s", line)) {
                err = -1;
-               fprintf(stderr, "Unexpected extra pprint output: %s\n", line);
                goto done;
        }
 
@@ -1551,9 +1803,9 @@ static int test_pprint(void)
 
 done:
        if (!err)
-               fprintf(stderr, "OK\n");
+               fprintf(stderr, "OK");
        if (*btf_log_buf && (err || args.always_log))
-               fprintf(stderr, "%s\n", btf_log_buf);
+               fprintf(stderr, "\n%s", btf_log_buf);
        if (btf_fd != -1)
                close(btf_fd);
        if (map_fd != -1)
@@ -1634,6 +1886,12 @@ static int parse_args(int argc, char **argv)
        return 0;
 }
 
+static void print_summary(void)
+{
+       fprintf(stderr, "PASS:%u SKIP:%u FAIL:%u\n",
+               pass_cnt - skip_cnt, skip_cnt, error_cnt);
+}
+
 int main(int argc, char **argv)
 {
        int err = 0;
@@ -1655,15 +1913,17 @@ int main(int argc, char **argv)
                err |= test_file();
 
        if (args.pprint_test)
-               err |= test_pprint();
+               err |= count_result(test_pprint());
 
        if (args.raw_test || args.get_info_test || args.file_test ||
            args.pprint_test)
-               return err;
+               goto done;
 
        err |= test_raw();
        err |= test_get_info();
        err |= test_file();
 
+done:
+       print_summary();
        return err;
 }
index ed197ee..3ecf733 100644 (file)
@@ -1272,6 +1272,139 @@ out:
        return;
 }
 
+static void test_stacktrace_build_id_nmi(void)
+{
+       int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
+       const char *file = "./test_stacktrace_build_id.o";
+       int err, pmu_fd, prog_fd;
+       struct perf_event_attr attr = {
+               .sample_freq = 5000,
+               .freq = 1,
+               .type = PERF_TYPE_HARDWARE,
+               .config = PERF_COUNT_HW_CPU_CYCLES,
+       };
+       __u32 key, previous_key, val, duration = 0;
+       struct bpf_object *obj;
+       char buf[256];
+       int i, j;
+       struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];
+       int build_id_matches = 0;
+
+       err = bpf_prog_load(file, BPF_PROG_TYPE_PERF_EVENT, &obj, &prog_fd);
+       if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
+               return;
+
+       pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
+                        0 /* cpu 0 */, -1 /* group id */,
+                        0 /* flags */);
+       if (CHECK(pmu_fd < 0, "perf_event_open",
+                 "err %d errno %d. Does the test host support PERF_COUNT_HW_CPU_CYCLES?\n",
+                 pmu_fd, errno))
+               goto close_prog;
+
+       err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
+       if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n",
+                 err, errno))
+               goto close_pmu;
+
+       err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
+       if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n",
+                 err, errno))
+               goto disable_pmu;
+
+       /* find map fds */
+       control_map_fd = bpf_find_map(__func__, obj, "control_map");
+       if (CHECK(control_map_fd < 0, "bpf_find_map control_map",
+                 "err %d errno %d\n", err, errno))
+               goto disable_pmu;
+
+       stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap");
+       if (CHECK(stackid_hmap_fd < 0, "bpf_find_map stackid_hmap",
+                 "err %d errno %d\n", err, errno))
+               goto disable_pmu;
+
+       stackmap_fd = bpf_find_map(__func__, obj, "stackmap");
+       if (CHECK(stackmap_fd < 0, "bpf_find_map stackmap", "err %d errno %d\n",
+                 err, errno))
+               goto disable_pmu;
+
+       stack_amap_fd = bpf_find_map(__func__, obj, "stack_amap");
+       if (CHECK(stack_amap_fd < 0, "bpf_find_map stack_amap",
+                 "err %d errno %d\n", err, errno))
+               goto disable_pmu;
+
+       assert(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")
+              == 0);
+       assert(system("taskset 0x1 ./urandom_read 100000") == 0);
+       /* disable stack trace collection */
+       key = 0;
+       val = 1;
+       bpf_map_update_elem(control_map_fd, &key, &val, 0);
+
+       /* for every element in stackid_hmap, we can find a corresponding one
+        * in stackmap, and vise versa.
+        */
+       err = compare_map_keys(stackid_hmap_fd, stackmap_fd);
+       if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap",
+                 "err %d errno %d\n", err, errno))
+               goto disable_pmu;
+
+       err = compare_map_keys(stackmap_fd, stackid_hmap_fd);
+       if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap",
+                 "err %d errno %d\n", err, errno))
+               goto disable_pmu;
+
+       err = extract_build_id(buf, 256);
+
+       if (CHECK(err, "get build_id with readelf",
+                 "err %d errno %d\n", err, errno))
+               goto disable_pmu;
+
+       err = bpf_map_get_next_key(stackmap_fd, NULL, &key);
+       if (CHECK(err, "get_next_key from stackmap",
+                 "err %d, errno %d\n", err, errno))
+               goto disable_pmu;
+
+       do {
+               char build_id[64];
+
+               err = bpf_map_lookup_elem(stackmap_fd, &key, id_offs);
+               if (CHECK(err, "lookup_elem from stackmap",
+                         "err %d, errno %d\n", err, errno))
+                       goto disable_pmu;
+               for (i = 0; i < PERF_MAX_STACK_DEPTH; ++i)
+                       if (id_offs[i].status == BPF_STACK_BUILD_ID_VALID &&
+                           id_offs[i].offset != 0) {
+                               for (j = 0; j < 20; ++j)
+                                       sprintf(build_id + 2 * j, "%02x",
+                                               id_offs[i].build_id[j] & 0xff);
+                               if (strstr(buf, build_id) != NULL)
+                                       build_id_matches = 1;
+                       }
+               previous_key = key;
+       } while (bpf_map_get_next_key(stackmap_fd, &previous_key, &key) == 0);
+
+       if (CHECK(build_id_matches < 1, "build id match",
+                 "Didn't find expected build ID from the map\n"))
+               goto disable_pmu;
+
+       /*
+        * We intentionally skip compare_stack_ips(). This is because we
+        * only support one in_nmi() ips-to-build_id translation per cpu
+        * at any time, thus stack_amap here will always fallback to
+        * BPF_STACK_BUILD_ID_IP;
+        */
+
+disable_pmu:
+       ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
+
+close_pmu:
+       close(pmu_fd);
+
+close_prog:
+       bpf_object__close(obj);
+}
+
 #define MAX_CNT_RAWTP  10ull
 #define MAX_STACK_RAWTP        100
 struct get_stack_trace_t {
@@ -1337,12 +1470,12 @@ static int get_stack_print_output(void *data, int size)
                        good_user_stack = true;
        }
        if (!good_kern_stack || !good_user_stack)
-               return PERF_EVENT_ERROR;
+               return LIBBPF_PERF_EVENT_ERROR;
 
        if (cnt == MAX_CNT_RAWTP)
-               return PERF_EVENT_DONE;
+               return LIBBPF_PERF_EVENT_DONE;
 
-       return PERF_EVENT_CONT;
+       return LIBBPF_PERF_EVENT_CONT;
 }
 
 static void test_get_stack_raw_tp(void)
@@ -1425,6 +1558,7 @@ int main(void)
        test_tp_attach_query();
        test_stacktrace_map();
        test_stacktrace_build_id();
+       test_stacktrace_build_id_nmi();
        test_stacktrace_map_raw_tp();
        test_get_stack_raw_tp();
 
diff --git a/tools/testing/selftests/bpf/test_sockhash_kern.c b/tools/testing/selftests/bpf/test_sockhash_kern.c
new file mode 100644 (file)
index 0000000..e675591
--- /dev/null
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Covalent IO, Inc. http://covalent.io
+#undef SOCKMAP
+#define TEST_MAP_TYPE BPF_MAP_TYPE_SOCKHASH
+#include "./test_sockmap_kern.h"
index 29c022d..eb17fae 100644 (file)
@@ -47,7 +47,8 @@ static void running_handler(int a);
 #define S1_PORT 10000
 #define S2_PORT 10001
 
-#define BPF_FILENAME "test_sockmap_kern.o"
+#define BPF_SOCKMAP_FILENAME "test_sockmap_kern.o"
+#define BPF_SOCKHASH_FILENAME "test_sockhash_kern.o"
 #define CG_PATH "/sockmap"
 
 /* global sockets */
@@ -1260,9 +1261,8 @@ int prog_type[] = {
        BPF_PROG_TYPE_SK_MSG,
 };
 
-static int populate_progs(void)
+static int populate_progs(char *bpf_file)
 {
-       char *bpf_file = BPF_FILENAME;
        struct bpf_program *prog;
        struct bpf_object *obj;
        int i = 0;
@@ -1306,11 +1306,11 @@ static int populate_progs(void)
        return 0;
 }
 
-static int test_suite(void)
+static int __test_suite(char *bpf_file)
 {
        int cg_fd, err;
 
-       err = populate_progs();
+       err = populate_progs(bpf_file);
        if (err < 0) {
                fprintf(stderr, "ERROR: (%i) load bpf failed\n", err);
                return err;
@@ -1347,17 +1347,30 @@ static int test_suite(void)
 
 out:
        printf("Summary: %i PASSED %i FAILED\n", passed, failed);
+       cleanup_cgroup_environment();
        close(cg_fd);
        return err;
 }
 
+static int test_suite(void)
+{
+       int err;
+
+       err = __test_suite(BPF_SOCKMAP_FILENAME);
+       if (err)
+               goto out;
+       err = __test_suite(BPF_SOCKHASH_FILENAME);
+out:
+       return err;
+}
+
 int main(int argc, char **argv)
 {
        struct rlimit r = {10 * 1024 * 1024, RLIM_INFINITY};
        int iov_count = 1, length = 1024, rate = 1;
        struct sockmap_options options = {0};
        int opt, longindex, err, cg_fd = 0;
-       char *bpf_file = BPF_FILENAME;
+       char *bpf_file = BPF_SOCKMAP_FILENAME;
        int test = PING_PONG;
 
        if (setrlimit(RLIMIT_MEMLOCK, &r)) {
@@ -1438,7 +1451,7 @@ int main(int argc, char **argv)
                return -1;
        }
 
-       err = populate_progs();
+       err = populate_progs(bpf_file);
        if (err) {
                fprintf(stderr, "populate program: (%s) %s\n",
                        bpf_file, strerror(errno));
index 33de97e..677b2ed 100644 (file)
@@ -1,340 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-// Copyright (c) 2017-2018 Covalent IO, Inc. http://covalent.io
-#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/in.h>
-#include <linux/udp.h>
-#include <linux/tcp.h>
-#include <linux/pkt_cls.h>
-#include <sys/socket.h>
-#include "bpf_helpers.h"
-#include "bpf_endian.h"
-
-/* Sockmap sample program connects a client and a backend together
- * using cgroups.
- *
- *    client:X <---> frontend:80 client:X <---> backend:80
- *
- * For simplicity we hard code values here and bind 1:1. The hard
- * coded values are part of the setup in sockmap.sh script that
- * is associated with this BPF program.
- *
- * The bpf_printk is verbose and prints information as connections
- * are established and verdicts are decided.
- */
-
-#define bpf_printk(fmt, ...)                                   \
-({                                                             \
-              char ____fmt[] = fmt;                            \
-              bpf_trace_printk(____fmt, sizeof(____fmt),       \
-                               ##__VA_ARGS__);                 \
-})
-
-struct bpf_map_def SEC("maps") sock_map = {
-       .type = BPF_MAP_TYPE_SOCKMAP,
-       .key_size = sizeof(int),
-       .value_size = sizeof(int),
-       .max_entries = 20,
-};
-
-struct bpf_map_def SEC("maps") sock_map_txmsg = {
-       .type = BPF_MAP_TYPE_SOCKMAP,
-       .key_size = sizeof(int),
-       .value_size = sizeof(int),
-       .max_entries = 20,
-};
-
-struct bpf_map_def SEC("maps") sock_map_redir = {
-       .type = BPF_MAP_TYPE_SOCKMAP,
-       .key_size = sizeof(int),
-       .value_size = sizeof(int),
-       .max_entries = 20,
-};
-
-struct bpf_map_def SEC("maps") sock_apply_bytes = {
-       .type = BPF_MAP_TYPE_ARRAY,
-       .key_size = sizeof(int),
-       .value_size = sizeof(int),
-       .max_entries = 1
-};
-
-struct bpf_map_def SEC("maps") sock_cork_bytes = {
-       .type = BPF_MAP_TYPE_ARRAY,
-       .key_size = sizeof(int),
-       .value_size = sizeof(int),
-       .max_entries = 1
-};
-
-struct bpf_map_def SEC("maps") sock_pull_bytes = {
-       .type = BPF_MAP_TYPE_ARRAY,
-       .key_size = sizeof(int),
-       .value_size = sizeof(int),
-       .max_entries = 2
-};
-
-struct bpf_map_def SEC("maps") sock_redir_flags = {
-       .type = BPF_MAP_TYPE_ARRAY,
-       .key_size = sizeof(int),
-       .value_size = sizeof(int),
-       .max_entries = 1
-};
-
-struct bpf_map_def SEC("maps") sock_skb_opts = {
-       .type = BPF_MAP_TYPE_ARRAY,
-       .key_size = sizeof(int),
-       .value_size = sizeof(int),
-       .max_entries = 1
-};
-
-SEC("sk_skb1")
-int bpf_prog1(struct __sk_buff *skb)
-{
-       return skb->len;
-}
-
-SEC("sk_skb2")
-int bpf_prog2(struct __sk_buff *skb)
-{
-       __u32 lport = skb->local_port;
-       __u32 rport = skb->remote_port;
-       int len, *f, ret, zero = 0;
-       __u64 flags = 0;
-
-       if (lport == 10000)
-               ret = 10;
-       else
-               ret = 1;
-
-       len = (__u32)skb->data_end - (__u32)skb->data;
-       f = bpf_map_lookup_elem(&sock_skb_opts, &zero);
-       if (f && *f) {
-               ret = 3;
-               flags = *f;
-       }
-
-       bpf_printk("sk_skb2: redirect(%iB) flags=%i\n",
-                  len, flags);
-       return bpf_sk_redirect_map(skb, &sock_map, ret, flags);
-}
-
-SEC("sockops")
-int bpf_sockmap(struct bpf_sock_ops *skops)
-{
-       __u32 lport, rport;
-       int op, err = 0, index, key, ret;
-
-
-       op = (int) skops->op;
-
-       switch (op) {
-       case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
-               lport = skops->local_port;
-               rport = skops->remote_port;
-
-               if (lport == 10000) {
-                       ret = 1;
-                       err = bpf_sock_map_update(skops, &sock_map, &ret,
-                                                 BPF_NOEXIST);
-                       bpf_printk("passive(%i -> %i) map ctx update err: %d\n",
-                                  lport, bpf_ntohl(rport), err);
-               }
-               break;
-       case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
-               lport = skops->local_port;
-               rport = skops->remote_port;
-
-               if (bpf_ntohl(rport) == 10001) {
-                       ret = 10;
-                       err = bpf_sock_map_update(skops, &sock_map, &ret,
-                                                 BPF_NOEXIST);
-                       bpf_printk("active(%i -> %i) map ctx update err: %d\n",
-                                  lport, bpf_ntohl(rport), err);
-               }
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-SEC("sk_msg1")
-int bpf_prog4(struct sk_msg_md *msg)
-{
-       int *bytes, zero = 0, one = 1;
-       int *start, *end;
-
-       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
-       if (bytes)
-               bpf_msg_apply_bytes(msg, *bytes);
-       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
-       if (bytes)
-               bpf_msg_cork_bytes(msg, *bytes);
-       start = bpf_map_lookup_elem(&sock_pull_bytes, &zero);
-       end = bpf_map_lookup_elem(&sock_pull_bytes, &one);
-       if (start && end)
-               bpf_msg_pull_data(msg, *start, *end, 0);
-       return SK_PASS;
-}
-
-SEC("sk_msg2")
-int bpf_prog5(struct sk_msg_md *msg)
-{
-       int err1 = -1, err2 = -1, zero = 0, one = 1;
-       int *bytes, *start, *end, len1, len2;
-
-       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
-       if (bytes)
-               err1 = bpf_msg_apply_bytes(msg, *bytes);
-       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
-       if (bytes)
-               err2 = bpf_msg_cork_bytes(msg, *bytes);
-       len1 = (__u64)msg->data_end - (__u64)msg->data;
-       start = bpf_map_lookup_elem(&sock_pull_bytes, &zero);
-       end = bpf_map_lookup_elem(&sock_pull_bytes, &one);
-       if (start && end) {
-               int err;
-
-               bpf_printk("sk_msg2: pull(%i:%i)\n",
-                          start ? *start : 0, end ? *end : 0);
-               err = bpf_msg_pull_data(msg, *start, *end, 0);
-               if (err)
-                       bpf_printk("sk_msg2: pull_data err %i\n",
-                                  err);
-               len2 = (__u64)msg->data_end - (__u64)msg->data;
-               bpf_printk("sk_msg2: length update %i->%i\n",
-                          len1, len2);
-       }
-       bpf_printk("sk_msg2: data length %i err1 %i err2 %i\n",
-                  len1, err1, err2);
-       return SK_PASS;
-}
-
-SEC("sk_msg3")
-int bpf_prog6(struct sk_msg_md *msg)
-{
-       int *bytes, zero = 0, one = 1, key = 0;
-       int *start, *end, *f;
-       __u64 flags = 0;
-
-       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
-       if (bytes)
-               bpf_msg_apply_bytes(msg, *bytes);
-       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
-       if (bytes)
-               bpf_msg_cork_bytes(msg, *bytes);
-       start = bpf_map_lookup_elem(&sock_pull_bytes, &zero);
-       end = bpf_map_lookup_elem(&sock_pull_bytes, &one);
-       if (start && end)
-               bpf_msg_pull_data(msg, *start, *end, 0);
-       f = bpf_map_lookup_elem(&sock_redir_flags, &zero);
-       if (f && *f) {
-               key = 2;
-               flags = *f;
-       }
-       return bpf_msg_redirect_map(msg, &sock_map_redir, key, flags);
-}
-
-SEC("sk_msg4")
-int bpf_prog7(struct sk_msg_md *msg)
-{
-       int err1 = 0, err2 = 0, zero = 0, one = 1, key = 0;
-       int *f, *bytes, *start, *end, len1, len2;
-       __u64 flags = 0;
-
-               int err;
-       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
-       if (bytes)
-               err1 = bpf_msg_apply_bytes(msg, *bytes);
-       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
-       if (bytes)
-               err2 = bpf_msg_cork_bytes(msg, *bytes);
-       len1 = (__u64)msg->data_end - (__u64)msg->data;
-       start = bpf_map_lookup_elem(&sock_pull_bytes, &zero);
-       end = bpf_map_lookup_elem(&sock_pull_bytes, &one);
-       if (start && end) {
-
-               bpf_printk("sk_msg2: pull(%i:%i)\n",
-                          start ? *start : 0, end ? *end : 0);
-               err = bpf_msg_pull_data(msg, *start, *end, 0);
-               if (err)
-                       bpf_printk("sk_msg2: pull_data err %i\n",
-                                  err);
-               len2 = (__u64)msg->data_end - (__u64)msg->data;
-               bpf_printk("sk_msg2: length update %i->%i\n",
-                          len1, len2);
-       }
-       f = bpf_map_lookup_elem(&sock_redir_flags, &zero);
-       if (f && *f) {
-               key = 2;
-               flags = *f;
-       }
-       bpf_printk("sk_msg3: redirect(%iB) flags=%i err=%i\n",
-                  len1, flags, err1 ? err1 : err2);
-       err = bpf_msg_redirect_map(msg, &sock_map_redir, key, flags);
-       bpf_printk("sk_msg3: err %i\n", err);
-       return err;
-}
-
-SEC("sk_msg5")
-int bpf_prog8(struct sk_msg_md *msg)
-{
-       void *data_end = (void *)(long) msg->data_end;
-       void *data = (void *)(long) msg->data;
-       int ret = 0, *bytes, zero = 0;
-
-       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
-       if (bytes) {
-               ret = bpf_msg_apply_bytes(msg, *bytes);
-               if (ret)
-                       return SK_DROP;
-       } else {
-               return SK_DROP;
-       }
-       return SK_PASS;
-}
-SEC("sk_msg6")
-int bpf_prog9(struct sk_msg_md *msg)
-{
-       void *data_end = (void *)(long) msg->data_end;
-       void *data = (void *)(long) msg->data;
-       int ret = 0, *bytes, zero = 0;
-
-       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
-       if (bytes) {
-               if (((__u64)data_end - (__u64)data) >= *bytes)
-                       return SK_PASS;
-               ret = bpf_msg_cork_bytes(msg, *bytes);
-               if (ret)
-                       return SK_DROP;
-       }
-       return SK_PASS;
-}
-
-SEC("sk_msg7")
-int bpf_prog10(struct sk_msg_md *msg)
-{
-       int *bytes, zero = 0, one = 1;
-       int *start, *end;
-
-       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
-       if (bytes)
-               bpf_msg_apply_bytes(msg, *bytes);
-       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
-       if (bytes)
-               bpf_msg_cork_bytes(msg, *bytes);
-       start = bpf_map_lookup_elem(&sock_pull_bytes, &zero);
-       end = bpf_map_lookup_elem(&sock_pull_bytes, &one);
-       if (start && end)
-               bpf_msg_pull_data(msg, *start, *end, 0);
-
-       return SK_DROP;
-}
-
-int _version SEC("version") = 1;
-char _license[] SEC("license") = "GPL";
+// Copyright (c) 2018 Covalent IO, Inc. http://covalent.io
+#define SOCKMAP
+#define TEST_MAP_TYPE BPF_MAP_TYPE_SOCKMAP
+#include "./test_sockmap_kern.h"
diff --git a/tools/testing/selftests/bpf/test_sockmap_kern.h b/tools/testing/selftests/bpf/test_sockmap_kern.h
new file mode 100644 (file)
index 0000000..8e8e417
--- /dev/null
@@ -0,0 +1,363 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2017-2018 Covalent IO, Inc. http://covalent.io */
+#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/in.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/pkt_cls.h>
+#include <sys/socket.h>
+#include "bpf_helpers.h"
+#include "bpf_endian.h"
+
+/* Sockmap sample program connects a client and a backend together
+ * using cgroups.
+ *
+ *    client:X <---> frontend:80 client:X <---> backend:80
+ *
+ * For simplicity we hard code values here and bind 1:1. The hard
+ * coded values are part of the setup in sockmap.sh script that
+ * is associated with this BPF program.
+ *
+ * The bpf_printk is verbose and prints information as connections
+ * are established and verdicts are decided.
+ */
+
+#define bpf_printk(fmt, ...)                                   \
+({                                                             \
+              char ____fmt[] = fmt;                            \
+              bpf_trace_printk(____fmt, sizeof(____fmt),       \
+                               ##__VA_ARGS__);                 \
+})
+
+struct bpf_map_def SEC("maps") sock_map = {
+       .type = TEST_MAP_TYPE,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 20,
+};
+
+struct bpf_map_def SEC("maps") sock_map_txmsg = {
+       .type = TEST_MAP_TYPE,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 20,
+};
+
+struct bpf_map_def SEC("maps") sock_map_redir = {
+       .type = TEST_MAP_TYPE,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 20,
+};
+
+struct bpf_map_def SEC("maps") sock_apply_bytes = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 1
+};
+
+struct bpf_map_def SEC("maps") sock_cork_bytes = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 1
+};
+
+struct bpf_map_def SEC("maps") sock_pull_bytes = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 2
+};
+
+struct bpf_map_def SEC("maps") sock_redir_flags = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 1
+};
+
+struct bpf_map_def SEC("maps") sock_skb_opts = {
+       .type = BPF_MAP_TYPE_ARRAY,
+       .key_size = sizeof(int),
+       .value_size = sizeof(int),
+       .max_entries = 1
+};
+
+SEC("sk_skb1")
+int bpf_prog1(struct __sk_buff *skb)
+{
+       return skb->len;
+}
+
+SEC("sk_skb2")
+int bpf_prog2(struct __sk_buff *skb)
+{
+       __u32 lport = skb->local_port;
+       __u32 rport = skb->remote_port;
+       int len, *f, ret, zero = 0;
+       __u64 flags = 0;
+
+       if (lport == 10000)
+               ret = 10;
+       else
+               ret = 1;
+
+       len = (__u32)skb->data_end - (__u32)skb->data;
+       f = bpf_map_lookup_elem(&sock_skb_opts, &zero);
+       if (f && *f) {
+               ret = 3;
+               flags = *f;
+       }
+
+       bpf_printk("sk_skb2: redirect(%iB) flags=%i\n",
+                  len, flags);
+#ifdef SOCKMAP
+       return bpf_sk_redirect_map(skb, &sock_map, ret, flags);
+#else
+       return bpf_sk_redirect_hash(skb, &sock_map, &ret, flags);
+#endif
+
+}
+
+SEC("sockops")
+int bpf_sockmap(struct bpf_sock_ops *skops)
+{
+       __u32 lport, rport;
+       int op, err = 0, index, key, ret;
+
+
+       op = (int) skops->op;
+
+       switch (op) {
+       case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
+               lport = skops->local_port;
+               rport = skops->remote_port;
+
+               if (lport == 10000) {
+                       ret = 1;
+#ifdef SOCKMAP
+                       err = bpf_sock_map_update(skops, &sock_map, &ret,
+                                                 BPF_NOEXIST);
+#else
+                       err = bpf_sock_hash_update(skops, &sock_map, &ret,
+                                                  BPF_NOEXIST);
+#endif
+                       bpf_printk("passive(%i -> %i) map ctx update err: %d\n",
+                                  lport, bpf_ntohl(rport), err);
+               }
+               break;
+       case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
+               lport = skops->local_port;
+               rport = skops->remote_port;
+
+               if (bpf_ntohl(rport) == 10001) {
+                       ret = 10;
+#ifdef SOCKMAP
+                       err = bpf_sock_map_update(skops, &sock_map, &ret,
+                                                 BPF_NOEXIST);
+#else
+                       err = bpf_sock_hash_update(skops, &sock_map, &ret,
+                                                  BPF_NOEXIST);
+#endif
+                       bpf_printk("active(%i -> %i) map ctx update err: %d\n",
+                                  lport, bpf_ntohl(rport), err);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+SEC("sk_msg1")
+int bpf_prog4(struct sk_msg_md *msg)
+{
+       int *bytes, zero = 0, one = 1;
+       int *start, *end;
+
+       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
+       if (bytes)
+               bpf_msg_apply_bytes(msg, *bytes);
+       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
+       if (bytes)
+               bpf_msg_cork_bytes(msg, *bytes);
+       start = bpf_map_lookup_elem(&sock_pull_bytes, &zero);
+       end = bpf_map_lookup_elem(&sock_pull_bytes, &one);
+       if (start && end)
+               bpf_msg_pull_data(msg, *start, *end, 0);
+       return SK_PASS;
+}
+
+SEC("sk_msg2")
+int bpf_prog5(struct sk_msg_md *msg)
+{
+       int err1 = -1, err2 = -1, zero = 0, one = 1;
+       int *bytes, *start, *end, len1, len2;
+
+       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
+       if (bytes)
+               err1 = bpf_msg_apply_bytes(msg, *bytes);
+       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
+       if (bytes)
+               err2 = bpf_msg_cork_bytes(msg, *bytes);
+       len1 = (__u64)msg->data_end - (__u64)msg->data;
+       start = bpf_map_lookup_elem(&sock_pull_bytes, &zero);
+       end = bpf_map_lookup_elem(&sock_pull_bytes, &one);
+       if (start && end) {
+               int err;
+
+               bpf_printk("sk_msg2: pull(%i:%i)\n",
+                          start ? *start : 0, end ? *end : 0);
+               err = bpf_msg_pull_data(msg, *start, *end, 0);
+               if (err)
+                       bpf_printk("sk_msg2: pull_data err %i\n",
+                                  err);
+               len2 = (__u64)msg->data_end - (__u64)msg->data;
+               bpf_printk("sk_msg2: length update %i->%i\n",
+                          len1, len2);
+       }
+       bpf_printk("sk_msg2: data length %i err1 %i err2 %i\n",
+                  len1, err1, err2);
+       return SK_PASS;
+}
+
+SEC("sk_msg3")
+int bpf_prog6(struct sk_msg_md *msg)
+{
+       int *bytes, zero = 0, one = 1, key = 0;
+       int *start, *end, *f;
+       __u64 flags = 0;
+
+       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
+       if (bytes)
+               bpf_msg_apply_bytes(msg, *bytes);
+       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
+       if (bytes)
+               bpf_msg_cork_bytes(msg, *bytes);
+       start = bpf_map_lookup_elem(&sock_pull_bytes, &zero);
+       end = bpf_map_lookup_elem(&sock_pull_bytes, &one);
+       if (start && end)
+               bpf_msg_pull_data(msg, *start, *end, 0);
+       f = bpf_map_lookup_elem(&sock_redir_flags, &zero);
+       if (f && *f) {
+               key = 2;
+               flags = *f;
+       }
+#ifdef SOCKMAP
+       return bpf_msg_redirect_map(msg, &sock_map_redir, key, flags);
+#else
+       return bpf_msg_redirect_hash(msg, &sock_map_redir, &key, flags);
+#endif
+}
+
+SEC("sk_msg4")
+int bpf_prog7(struct sk_msg_md *msg)
+{
+       int err1 = 0, err2 = 0, zero = 0, one = 1, key = 0;
+       int *f, *bytes, *start, *end, len1, len2;
+       __u64 flags = 0;
+
+               int err;
+       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
+       if (bytes)
+               err1 = bpf_msg_apply_bytes(msg, *bytes);
+       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
+       if (bytes)
+               err2 = bpf_msg_cork_bytes(msg, *bytes);
+       len1 = (__u64)msg->data_end - (__u64)msg->data;
+       start = bpf_map_lookup_elem(&sock_pull_bytes, &zero);
+       end = bpf_map_lookup_elem(&sock_pull_bytes, &one);
+       if (start && end) {
+
+               bpf_printk("sk_msg2: pull(%i:%i)\n",
+                          start ? *start : 0, end ? *end : 0);
+               err = bpf_msg_pull_data(msg, *start, *end, 0);
+               if (err)
+                       bpf_printk("sk_msg2: pull_data err %i\n",
+                                  err);
+               len2 = (__u64)msg->data_end - (__u64)msg->data;
+               bpf_printk("sk_msg2: length update %i->%i\n",
+                          len1, len2);
+       }
+       f = bpf_map_lookup_elem(&sock_redir_flags, &zero);
+       if (f && *f) {
+               key = 2;
+               flags = *f;
+       }
+       bpf_printk("sk_msg3: redirect(%iB) flags=%i err=%i\n",
+                  len1, flags, err1 ? err1 : err2);
+#ifdef SOCKMAP
+       err = bpf_msg_redirect_map(msg, &sock_map_redir, key, flags);
+#else
+       err = bpf_msg_redirect_hash(msg, &sock_map_redir, &key, flags);
+#endif
+       bpf_printk("sk_msg3: err %i\n", err);
+       return err;
+}
+
+SEC("sk_msg5")
+int bpf_prog8(struct sk_msg_md *msg)
+{
+       void *data_end = (void *)(long) msg->data_end;
+       void *data = (void *)(long) msg->data;
+       int ret = 0, *bytes, zero = 0;
+
+       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
+       if (bytes) {
+               ret = bpf_msg_apply_bytes(msg, *bytes);
+               if (ret)
+                       return SK_DROP;
+       } else {
+               return SK_DROP;
+       }
+       return SK_PASS;
+}
+SEC("sk_msg6")
+int bpf_prog9(struct sk_msg_md *msg)
+{
+       void *data_end = (void *)(long) msg->data_end;
+       void *data = (void *)(long) msg->data;
+       int ret = 0, *bytes, zero = 0;
+
+       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
+       if (bytes) {
+               if (((__u64)data_end - (__u64)data) >= *bytes)
+                       return SK_PASS;
+               ret = bpf_msg_cork_bytes(msg, *bytes);
+               if (ret)
+                       return SK_DROP;
+       }
+       return SK_PASS;
+}
+
+SEC("sk_msg7")
+int bpf_prog10(struct sk_msg_md *msg)
+{
+       int *bytes, zero = 0, one = 1;
+       int *start, *end;
+
+       bytes = bpf_map_lookup_elem(&sock_apply_bytes, &zero);
+       if (bytes)
+               bpf_msg_apply_bytes(msg, *bytes);
+       bytes = bpf_map_lookup_elem(&sock_cork_bytes, &zero);
+       if (bytes)
+               bpf_msg_cork_bytes(msg, *bytes);
+       start = bpf_map_lookup_elem(&sock_pull_bytes, &zero);
+       end = bpf_map_lookup_elem(&sock_pull_bytes, &one);
+       if (start && end)
+               bpf_msg_pull_data(msg, *start, *end, 0);
+
+       return SK_DROP;
+}
+
+int _version SEC("version") = 1;
+char _license[] SEC("license") = "GPL";
index 275b457..94498ea 100644 (file)
@@ -41,6 +41,7 @@
 # endif
 #endif
 #include "bpf_rlimit.h"
+#include "bpf_rand.h"
 #include "../../../include/linux/filter.h"
 
 #ifndef ARRAY_SIZE
@@ -152,6 +153,30 @@ static void bpf_fill_jump_around_ld_abs(struct bpf_test *self)
        insn[i] = BPF_EXIT_INSN();
 }
 
+static void bpf_fill_rand_ld_dw(struct bpf_test *self)
+{
+       struct bpf_insn *insn = self->insns;
+       uint64_t res = 0;
+       int i = 0;
+
+       insn[i++] = BPF_MOV32_IMM(BPF_REG_0, 0);
+       while (i < self->retval) {
+               uint64_t val = bpf_semi_rand_get();
+               struct bpf_insn tmp[2] = { BPF_LD_IMM64(BPF_REG_1, val) };
+
+               res ^= val;
+               insn[i++] = tmp[0];
+               insn[i++] = tmp[1];
+               insn[i++] = BPF_ALU64_REG(BPF_XOR, BPF_REG_0, BPF_REG_1);
+       }
+       insn[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_0);
+       insn[i++] = BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 32);
+       insn[i++] = BPF_ALU64_REG(BPF_XOR, BPF_REG_0, BPF_REG_1);
+       insn[i] = BPF_EXIT_INSN();
+       res ^= (res >> 32);
+       self->retval = (uint32_t)res;
+}
+
 static struct bpf_test tests[] = {
        {
                "add+sub+mul",
@@ -11974,6 +11999,42 @@ static struct bpf_test tests[] = {
                .result = ACCEPT,
                .retval = 10,
        },
+       {
+               "ld_dw: xor semi-random 64 bit imms, test 1",
+               .insns = { },
+               .data = { },
+               .fill_helper = bpf_fill_rand_ld_dw,
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 4090,
+       },
+       {
+               "ld_dw: xor semi-random 64 bit imms, test 2",
+               .insns = { },
+               .data = { },
+               .fill_helper = bpf_fill_rand_ld_dw,
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 2047,
+       },
+       {
+               "ld_dw: xor semi-random 64 bit imms, test 3",
+               .insns = { },
+               .data = { },
+               .fill_helper = bpf_fill_rand_ld_dw,
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 511,
+       },
+       {
+               "ld_dw: xor semi-random 64 bit imms, test 4",
+               .insns = { },
+               .data = { },
+               .fill_helper = bpf_fill_rand_ld_dw,
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 5,
+       },
 };
 
 static int probe_filter_length(const struct bpf_insn *fp)
@@ -12276,6 +12337,11 @@ static void get_unpriv_disabled()
        FILE *fd;
 
        fd = fopen("/proc/sys/"UNPRIV_SYSCTL, "r");
+       if (!fd) {
+               perror("fopen /proc/sys/"UNPRIV_SYSCTL);
+               unpriv_disabled = true;
+               return;
+       }
        if (fgets(buf, 2, fd) == buf && atoi(buf))
                unpriv_disabled = true;
        fclose(fd);
@@ -12346,5 +12412,6 @@ int main(int argc, char **argv)
                return EXIT_FAILURE;
        }
 
+       bpf_semi_rand_init();
        return do_test(unpriv, from, to);
 }
index ad025bd..8fb4fe8 100644 (file)
@@ -74,7 +74,7 @@ struct ksym *ksym_search(long key)
 
 static int page_size;
 static int page_cnt = 8;
-static volatile struct perf_event_mmap_page *header;
+static struct perf_event_mmap_page *header;
 
 int perf_event_mmap(int fd)
 {
@@ -107,74 +107,47 @@ struct perf_event_sample {
        char data[];
 };
 
-static int perf_event_read(perf_event_print_fn fn)
+static enum bpf_perf_event_ret bpf_perf_event_print(void *event, void *priv)
 {
-       __u64 data_tail = header->data_tail;
-       __u64 data_head = header->data_head;
-       __u64 buffer_size = page_cnt * page_size;
-       void *base, *begin, *end;
-       char buf[256];
+       struct perf_event_sample *e = event;
+       perf_event_print_fn fn = priv;
        int ret;
 
-       asm volatile("" ::: "memory"); /* in real code it should be smp_rmb() */
-       if (data_head == data_tail)
-               return PERF_EVENT_CONT;
-
-       base = ((char *)header) + page_size;
-
-       begin = base + data_tail % buffer_size;
-       end = base + data_head % buffer_size;
-
-       while (begin != end) {
-               struct perf_event_sample *e;
-
-               e = begin;
-               if (begin + e->header.size > base + buffer_size) {
-                       long len = base + buffer_size - begin;
-
-                       assert(len < e->header.size);
-                       memcpy(buf, begin, len);
-                       memcpy(buf + len, base, e->header.size - len);
-                       e = (void *) buf;
-                       begin = base + e->header.size - len;
-               } else if (begin + e->header.size == base + buffer_size) {
-                       begin = base;
-               } else {
-                       begin += e->header.size;
-               }
-
-               if (e->header.type == PERF_RECORD_SAMPLE) {
-                       ret = fn(e->data, e->size);
-                       if (ret != PERF_EVENT_CONT)
-                               return ret;
-               } else if (e->header.type == PERF_RECORD_LOST) {
-                       struct {
-                               struct perf_event_header header;
-                               __u64 id;
-                               __u64 lost;
-                       } *lost = (void *) e;
-                       printf("lost %lld events\n", lost->lost);
-               } else {
-                       printf("unknown event type=%d size=%d\n",
-                              e->header.type, e->header.size);
-               }
+       if (e->header.type == PERF_RECORD_SAMPLE) {
+               ret = fn(e->data, e->size);
+               if (ret != LIBBPF_PERF_EVENT_CONT)
+                       return ret;
+       } else if (e->header.type == PERF_RECORD_LOST) {
+               struct {
+                       struct perf_event_header header;
+                       __u64 id;
+                       __u64 lost;
+               } *lost = (void *) e;
+               printf("lost %lld events\n", lost->lost);
+       } else {
+               printf("unknown event type=%d size=%d\n",
+                      e->header.type, e->header.size);
        }
 
-       __sync_synchronize(); /* smp_mb() */
-       header->data_tail = data_head;
-       return PERF_EVENT_CONT;
+       return LIBBPF_PERF_EVENT_CONT;
 }
 
 int perf_event_poller(int fd, perf_event_print_fn output_fn)
 {
-       int ret;
+       enum bpf_perf_event_ret ret;
+       void *buf = NULL;
+       size_t len = 0;
 
        for (;;) {
                perf_event_poll(fd);
-               ret = perf_event_read(output_fn);
-               if (ret != PERF_EVENT_CONT)
-                       return ret;
+               ret = bpf_perf_event_read_simple(header, page_cnt * page_size,
+                                                page_size, &buf, &len,
+                                                bpf_perf_event_print,
+                                                output_fn);
+               if (ret != LIBBPF_PERF_EVENT_CONT)
+                       break;
        }
+       free(buf);
 
-       return PERF_EVENT_DONE;
+       return ret;
 }
index fe3eefd..36d90e3 100644 (file)
@@ -2,6 +2,8 @@
 #ifndef __TRACE_HELPER_H
 #define __TRACE_HELPER_H
 
+#include <libbpf.h>
+
 struct ksym {
        long addr;
        char *name;
@@ -10,14 +12,9 @@ struct ksym {
 int load_kallsyms(void);
 struct ksym *ksym_search(long key);
 
-typedef int (*perf_event_print_fn)(void *data, int size);
-
-/* return code for perf_event_print_fn */
-#define PERF_EVENT_DONE                0
-#define PERF_EVENT_ERROR       -1
-#define PERF_EVENT_CONT                -2
+typedef enum bpf_perf_event_ret (*perf_event_print_fn)(void *data, int size);
 
 int perf_event_mmap(int fd);
-/* return PERF_EVENT_DONE or PERF_EVENT_ERROR */
+/* return LIBBPF_PERF_EVENT_DONE or LIBBPF_PERF_EVENT_ERROR */
 int perf_event_poller(int fd, perf_event_print_fn output_fn);
 #endif
index 4acfdeb..9de8b7c 100644 (file)
@@ -6,15 +6,21 @@
 #include <stdlib.h>
 
 #define BUF_SIZE 256
-int main(void)
+
+int main(int argc, char *argv[])
 {
        int fd = open("/dev/urandom", O_RDONLY);
        int i;
        char buf[BUF_SIZE];
+       int count = 4;
 
        if (fd < 0)
                return 1;
-       for (i = 0; i < 4; ++i)
+
+       if (argc == 2)
+               count = atoi(argv[1]);
+
+       for (i = 0; i < count; ++i)
                read(fd, buf, BUF_SIZE);
 
        close(fd);
index 2ddcc96..d9d0031 100644 (file)
@@ -15,7 +15,7 @@ LIBKVM += $(LIBKVM_$(UNAME_M))
 
 INSTALL_HDR_PATH = $(top_srcdir)/usr
 LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/
-CFLAGS += -O2 -g -std=gnu99 -I$(LINUX_HDR_PATH) -Iinclude -I$(<D)
+CFLAGS += -O2 -g -std=gnu99 -I$(LINUX_HDR_PATH) -Iinclude -I$(<D) -I..
 
 # After inclusion, $(OUTPUT) is defined and
 # $(TEST_GEN_PROGS) starts with $(OUTPUT)/
index 7ab98e4..ac53730 100644 (file)
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include "kselftest.h"
 
 ssize_t test_write(int fd, const void *buf, size_t count);
 ssize_t test_read(int fd, void *buf, size_t count);
index 2cedfda..37e2a78 100644 (file)
@@ -50,8 +50,8 @@ int kvm_check_cap(long cap)
        int kvm_fd;
 
        kvm_fd = open(KVM_DEV_PATH, O_RDONLY);
-       TEST_ASSERT(kvm_fd >= 0, "open %s failed, rc: %i errno: %i",
-               KVM_DEV_PATH, kvm_fd, errno);
+       if (kvm_fd < 0)
+               exit(KSFT_SKIP);
 
        ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, cap);
        TEST_ASSERT(ret != -1, "KVM_CHECK_EXTENSION IOCTL failed,\n"
@@ -91,8 +91,8 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
 
        vm->mode = mode;
        kvm_fd = open(KVM_DEV_PATH, perm);
-       TEST_ASSERT(kvm_fd >= 0, "open %s failed, rc: %i errno: %i",
-               KVM_DEV_PATH, kvm_fd, errno);
+       if (kvm_fd < 0)
+               exit(KSFT_SKIP);
 
        /* Create VM. */
        vm->fd = ioctl(kvm_fd, KVM_CREATE_VM, NULL);
@@ -418,8 +418,8 @@ struct kvm_cpuid2 *kvm_get_supported_cpuid(void)
 
        cpuid = allocate_kvm_cpuid2();
        kvm_fd = open(KVM_DEV_PATH, O_RDONLY);
-       TEST_ASSERT(kvm_fd >= 0, "open %s failed, rc: %i errno: %i",
-               KVM_DEV_PATH, kvm_fd, errno);
+       if (kvm_fd < 0)
+               exit(KSFT_SKIP);
 
        ret = ioctl(kvm_fd, KVM_GET_SUPPORTED_CPUID, cpuid);
        TEST_ASSERT(ret == 0, "KVM_GET_SUPPORTED_CPUID failed %d %d\n",
@@ -675,8 +675,8 @@ static int vcpu_mmap_sz(void)
        int dev_fd, ret;
 
        dev_fd = open(KVM_DEV_PATH, O_RDONLY);
-       TEST_ASSERT(dev_fd >= 0, "%s open %s failed, rc: %i errno: %i",
-               __func__, KVM_DEV_PATH, dev_fd, errno);
+       if (dev_fd < 0)
+               exit(KSFT_SKIP);
 
        ret = ioctl(dev_fd, KVM_GET_VCPU_MMAP_SIZE, NULL);
        TEST_ASSERT(ret >= sizeof(struct kvm_run),
index 428e947..eae1ece 100644 (file)
@@ -85,6 +85,9 @@ static void compare_vcpu_events(struct kvm_vcpu_events *left,
 {
 }
 
+#define TEST_SYNC_FIELDS   (KVM_SYNC_X86_REGS|KVM_SYNC_X86_SREGS|KVM_SYNC_X86_EVENTS)
+#define INVALID_SYNC_FIELD 0x80000000
+
 int main(int argc, char *argv[])
 {
        struct kvm_vm *vm;
@@ -98,9 +101,14 @@ int main(int argc, char *argv[])
        setbuf(stdout, NULL);
 
        cap = kvm_check_cap(KVM_CAP_SYNC_REGS);
-       TEST_ASSERT((unsigned long)cap == KVM_SYNC_X86_VALID_FIELDS,
-                   "KVM_CAP_SYNC_REGS (0x%x) != KVM_SYNC_X86_VALID_FIELDS (0x%lx)\n",
-                   cap, KVM_SYNC_X86_VALID_FIELDS);
+       if ((cap & TEST_SYNC_FIELDS) != TEST_SYNC_FIELDS) {
+               fprintf(stderr, "KVM_CAP_SYNC_REGS not supported, skipping test\n");
+               exit(KSFT_SKIP);
+       }
+       if ((cap & INVALID_SYNC_FIELD) != 0) {
+               fprintf(stderr, "The \"invalid\" field is not invalid, skipping test\n");
+               exit(KSFT_SKIP);
+       }
 
        /* Create VM */
        vm = vm_create_default(VCPU_ID, guest_code);
@@ -108,7 +116,14 @@ int main(int argc, char *argv[])
        run = vcpu_state(vm, VCPU_ID);
 
        /* Request reading invalid register set from VCPU. */
-       run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS << 1;
+       run->kvm_valid_regs = INVALID_SYNC_FIELD;
+       rv = _vcpu_run(vm, VCPU_ID);
+       TEST_ASSERT(rv < 0 && errno == EINVAL,
+                   "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n",
+                   rv);
+       vcpu_state(vm, VCPU_ID)->kvm_valid_regs = 0;
+
+       run->kvm_valid_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS;
        rv = _vcpu_run(vm, VCPU_ID);
        TEST_ASSERT(rv < 0 && errno == EINVAL,
                    "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n",
@@ -116,7 +131,14 @@ int main(int argc, char *argv[])
        vcpu_state(vm, VCPU_ID)->kvm_valid_regs = 0;
 
        /* Request setting invalid register set into VCPU. */
-       run->kvm_dirty_regs = KVM_SYNC_X86_VALID_FIELDS << 1;
+       run->kvm_dirty_regs = INVALID_SYNC_FIELD;
+       rv = _vcpu_run(vm, VCPU_ID);
+       TEST_ASSERT(rv < 0 && errno == EINVAL,
+                   "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n",
+                   rv);
+       vcpu_state(vm, VCPU_ID)->kvm_dirty_regs = 0;
+
+       run->kvm_dirty_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS;
        rv = _vcpu_run(vm, VCPU_ID);
        TEST_ASSERT(rv < 0 && errno == EINVAL,
                    "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n",
@@ -125,7 +147,7 @@ int main(int argc, char *argv[])
 
        /* Request and verify all valid register sets. */
        /* TODO: BUILD TIME CHECK: TEST_ASSERT(KVM_SYNC_X86_NUM_FIELDS != 3); */
-       run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS;
+       run->kvm_valid_regs = TEST_SYNC_FIELDS;
        rv = _vcpu_run(vm, VCPU_ID);
        TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
                    "Unexpected exit reason: %u (%s),\n",
@@ -146,7 +168,7 @@ int main(int argc, char *argv[])
        run->s.regs.sregs.apic_base = 1 << 11;
        /* TODO run->s.regs.events.XYZ = ABC; */
 
-       run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS;
+       run->kvm_valid_regs = TEST_SYNC_FIELDS;
        run->kvm_dirty_regs = KVM_SYNC_X86_REGS | KVM_SYNC_X86_SREGS;
        rv = _vcpu_run(vm, VCPU_ID);
        TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
@@ -172,7 +194,7 @@ int main(int argc, char *argv[])
        /* Clear kvm_dirty_regs bits, verify new s.regs values are
         * overwritten with existing guest values.
         */
-       run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS;
+       run->kvm_valid_regs = TEST_SYNC_FIELDS;
        run->kvm_dirty_regs = 0;
        run->s.regs.regs.r11 = 0xDEADBEEF;
        rv = _vcpu_run(vm, VCPU_ID);
@@ -211,7 +233,7 @@ int main(int argc, char *argv[])
         * with kvm_sync_regs values.
         */
        run->kvm_valid_regs = 0;
-       run->kvm_dirty_regs = KVM_SYNC_X86_VALID_FIELDS;
+       run->kvm_dirty_regs = TEST_SYNC_FIELDS;
        run->s.regs.regs.r11 = 0xBBBB;
        rv = _vcpu_run(vm, VCPU_ID);
        TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
index 8f7f620..aaa6332 100644 (file)
@@ -189,8 +189,8 @@ int main(int argc, char *argv[])
        struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1);
 
        if (!(entry->ecx & CPUID_VMX)) {
-               printf("nested VMX not enabled, skipping test");
-               return 0;
+               fprintf(stderr, "nested VMX not enabled, skipping test\n");
+               exit(KSFT_SKIP);
        }
 
        vm = vm_create_default_vmx(VCPU_ID, (void *) l1_guest_code);
index e60dddb..7cb0f49 100644 (file)
@@ -6,7 +6,7 @@ CFLAGS += -I../../../../usr/include/
 
 TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh
 TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh udpgso.sh
-TEST_PROGS += udpgso_bench.sh
+TEST_PROGS += udpgso_bench.sh fib_rule_tests.sh
 TEST_PROGS_EXTENDED := in_netns.sh
 TEST_GEN_FILES =  socket
 TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy
diff --git a/tools/testing/selftests/net/fib_rule_tests.sh b/tools/testing/selftests/net/fib_rule_tests.sh
new file mode 100755 (executable)
index 0000000..d4cfb6a
--- /dev/null
@@ -0,0 +1,248 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# This test is for checking IPv4 and IPv6 FIB rules API
+
+ret=0
+
+PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
+IP="ip -netns testns"
+
+RTABLE=100
+GW_IP4=192.51.100.2
+SRC_IP=192.51.100.3
+GW_IP6=2001:db8:1::2
+SRC_IP6=2001:db8:1::3
+
+DEV_ADDR=192.51.100.1
+DEV=dummy0
+
+log_test()
+{
+       local rc=$1
+       local expected=$2
+       local msg="$3"
+
+       if [ ${rc} -eq ${expected} ]; then
+               nsuccess=$((nsuccess+1))
+               printf "\n    TEST: %-50s  [ OK ]\n" "${msg}"
+       else
+               nfail=$((nfail+1))
+               printf "\n    TEST: %-50s  [FAIL]\n" "${msg}"
+               if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+                       echo
+                       echo "hit enter to continue, 'q' to quit"
+                       read a
+                       [ "$a" = "q" ] && exit 1
+               fi
+       fi
+}
+
+log_section()
+{
+       echo
+       echo "######################################################################"
+       echo "TEST SECTION: $*"
+       echo "######################################################################"
+}
+
+setup()
+{
+       set -e
+       ip netns add testns
+       $IP link set dev lo up
+
+       $IP link add dummy0 type dummy
+       $IP link set dev dummy0 up
+       $IP address add 198.51.100.1/24 dev dummy0
+       $IP -6 address add 2001:db8:1::1/64 dev dummy0
+
+       set +e
+}
+
+cleanup()
+{
+       $IP link del dev dummy0 &> /dev/null
+       ip netns del testns
+}
+
+fib_check_iproute_support()
+{
+       ip rule help 2>&1 | grep -q $1
+       if [ $? -ne 0 ]; then
+               echo "SKIP: iproute2 iprule too old, missing $1 match"
+               return 1
+       fi
+
+       ip route get help 2>&1 | grep -q $2
+       if [ $? -ne 0 ]; then
+               echo "SKIP: iproute2 get route too old, missing $2 match"
+               return 1
+       fi
+
+       return 0
+}
+
+fib_rule6_del()
+{
+       $IP -6 rule del $1
+       log_test $? 0 "rule6 del $1"
+}
+
+fib_rule6_del_by_pref()
+{
+       pref=$($IP -6 rule show | grep "$1 lookup $TABLE" | cut -d ":" -f 1)
+       $IP -6 rule del pref $pref
+}
+
+fib_rule6_test_match_n_redirect()
+{
+       local match="$1"
+       local getmatch="$2"
+
+       $IP -6 rule add $match table $RTABLE
+       $IP -6 route get $GW_IP6 $getmatch | grep -q "table $RTABLE"
+       log_test $? 0 "rule6 check: $1"
+
+       fib_rule6_del_by_pref "$match"
+       log_test $? 0 "rule6 del by pref: $match"
+}
+
+fib_rule6_test()
+{
+       # setup the fib rule redirect route
+       $IP -6 route add table $RTABLE default via $GW_IP6 dev $DEV onlink
+
+       match="oif $DEV"
+       fib_rule6_test_match_n_redirect "$match" "$match" "oif redirect to table"
+
+       match="from $SRC_IP6 iif $DEV"
+       fib_rule6_test_match_n_redirect "$match" "$match" "iif redirect to table"
+
+       match="tos 0x10"
+       fib_rule6_test_match_n_redirect "$match" "$match" "tos redirect to table"
+
+       match="fwmark 0x64"
+       getmatch="mark 0x64"
+       fib_rule6_test_match_n_redirect "$match" "$getmatch" "fwmark redirect to table"
+
+       fib_check_iproute_support "uidrange" "uid"
+       if [ $? -eq 0 ]; then
+               match="uidrange 100-100"
+               getmatch="uid 100"
+               fib_rule6_test_match_n_redirect "$match" "$getmatch" "uid redirect to table"
+       fi
+
+       fib_check_iproute_support "sport" "sport"
+       if [ $? -eq 0 ]; then
+               match="sport 666 dport 777"
+               fib_rule6_test_match_n_redirect "$match" "$match" "sport and dport redirect to table"
+       fi
+
+       fib_check_iproute_support "ipproto" "ipproto"
+       if [ $? -eq 0 ]; then
+               match="ipproto tcp"
+               fib_rule6_test_match_n_redirect "$match" "$match" "ipproto match"
+       fi
+
+       fib_check_iproute_support "ipproto" "ipproto"
+       if [ $? -eq 0 ]; then
+               match="ipproto icmp"
+               fib_rule6_test_match_n_redirect "$match" "$match" "ipproto icmp match"
+       fi
+}
+
+fib_rule4_del()
+{
+       $IP rule del $1
+       log_test $? 0 "del $1"
+}
+
+fib_rule4_del_by_pref()
+{
+       pref=$($IP rule show | grep "$1 lookup $TABLE" | cut -d ":" -f 1)
+       $IP rule del pref $pref
+}
+
+fib_rule4_test_match_n_redirect()
+{
+       local match="$1"
+       local getmatch="$2"
+
+       $IP rule add $match table $RTABLE
+       $IP route get $GW_IP4 $getmatch | grep -q "table $RTABLE"
+       log_test $? 0 "rule4 check: $1"
+
+       fib_rule4_del_by_pref "$match"
+       log_test $? 0 "rule4 del by pref: $match"
+}
+
+fib_rule4_test()
+{
+       # setup the fib rule redirect route
+       $IP route add table $RTABLE default via $GW_IP4 dev $DEV onlink
+
+       match="oif $DEV"
+       fib_rule4_test_match_n_redirect "$match" "$match" "oif redirect to table"
+
+       match="from $SRC_IP iif $DEV"
+       fib_rule4_test_match_n_redirect "$match" "$match" "iif redirect to table"
+
+       match="tos 0x10"
+       fib_rule4_test_match_n_redirect "$match" "$match" "tos redirect to table"
+
+       match="fwmark 0x64"
+       getmatch="mark 0x64"
+       fib_rule4_test_match_n_redirect "$match" "$getmatch" "fwmark redirect to table"
+
+       fib_check_iproute_support "uidrange" "uid"
+       if [ $? -eq 0 ]; then
+               match="uidrange 100-100"
+               getmatch="uid 100"
+               fib_rule4_test_match_n_redirect "$match" "$getmatch" "uid redirect to table"
+       fi
+
+       fib_check_iproute_support "sport" "sport"
+       if [ $? -eq 0 ]; then
+               match="sport 666 dport 777"
+               fib_rule4_test_match_n_redirect "$match" "$match" "sport and dport redirect to table"
+       fi
+
+       fib_check_iproute_support "ipproto" "ipproto"
+       if [ $? -eq 0 ]; then
+               match="ipproto tcp"
+               fib_rule4_test_match_n_redirect "$match" "$match" "ipproto tcp match"
+       fi
+
+       fib_check_iproute_support "ipproto" "ipproto"
+       if [ $? -eq 0 ]; then
+               match="ipproto icmp"
+               fib_rule4_test_match_n_redirect "$match" "$match" "ipproto icmp match"
+       fi
+}
+
+run_fibrule_tests()
+{
+       log_section "IPv4 fib rule"
+       fib_rule4_test
+       log_section "IPv6 fib rule"
+       fib_rule6_test
+}
+
+if [ "$(id -u)" -ne 0 ];then
+       echo "SKIP: Need root privileges"
+       exit 0
+fi
+
+if [ ! -x "$(command -v ip)" ]; then
+       echo "SKIP: Could not run test without ip tool"
+       exit 0
+fi
+
+# start clean
+cleanup &> /dev/null
+setup
+run_fibrule_tests
+cleanup
+
+exit $ret
index 9164e60..e7d76fb 100755 (executable)
@@ -6,8 +6,10 @@
 
 ret=0
 
-VERBOSE=${VERBOSE:=0}
-PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
+TESTS="unregister down carrier nexthop ipv6_rt ipv4_rt"
+VERBOSE=0
+PAUSE_ON_FAIL=no
+PAUSE=no
 IP="ip -netns testns"
 
 log_test()
@@ -18,8 +20,10 @@ log_test()
 
        if [ ${rc} -eq ${expected} ]; then
                printf "    TEST: %-60s  [ OK ]\n" "${msg}"
+               nsuccess=$((nsuccess+1))
        else
                ret=1
+               nfail=$((nfail+1))
                printf "    TEST: %-60s  [FAIL]\n" "${msg}"
                if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
                echo
@@ -28,6 +32,13 @@ log_test()
                        [ "$a" = "q" ] && exit 1
                fi
        fi
+
+       if [ "${PAUSE}" = "yes" ]; then
+               echo
+               echo "hit enter to continue, 'q' to quit"
+               read a
+               [ "$a" = "q" ] && exit 1
+       fi
 }
 
 setup()
@@ -563,20 +574,649 @@ fib_nexthop_test()
 }
 
 ################################################################################
-#
+# Tests on route add and replace
+
+run_cmd()
+{
+       local cmd="$1"
+       local out
+       local stderr="2>/dev/null"
+
+       if [ "$VERBOSE" = "1" ]; then
+               printf "    COMMAND: $cmd\n"
+               stderr=
+       fi
+
+       out=$(eval $cmd $stderr)
+       rc=$?
+       if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+               echo "    $out"
+       fi
+
+       [ "$VERBOSE" = "1" ] && echo
+
+       return $rc
+}
+
+# add route for a prefix, flushing any existing routes first
+# expected to be the first step of a test
+add_route6()
+{
+       local pfx="$1"
+       local nh="$2"
+       local out
+
+       if [ "$VERBOSE" = "1" ]; then
+               echo
+               echo "    ##################################################"
+               echo
+       fi
+
+       run_cmd "$IP -6 ro flush ${pfx}"
+       [ $? -ne 0 ] && exit 1
+
+       out=$($IP -6 ro ls match ${pfx})
+       if [ -n "$out" ]; then
+               echo "Failed to flush routes for prefix used for tests."
+               exit 1
+       fi
+
+       run_cmd "$IP -6 ro add ${pfx} ${nh}"
+       if [ $? -ne 0 ]; then
+               echo "Failed to add initial route for test."
+               exit 1
+       fi
+}
+
+# add initial route - used in replace route tests
+add_initial_route6()
+{
+       add_route6 "2001:db8:104::/64" "$1"
+}
+
+check_route6()
+{
+       local pfx="2001:db8:104::/64"
+       local expected="$1"
+       local out
+       local rc=0
+
+       out=$($IP -6 ro ls match ${pfx} | sed -e 's/ pref medium//')
+       if [ -z "${out}" ]; then
+               if [ "$VERBOSE" = "1" ]; then
+                       printf "\nNo route entry found\n"
+                       printf "Expected:\n"
+                       printf "    ${expected}\n"
+               fi
+               return 1
+       fi
+
+       # tricky way to convert output to 1-line without ip's
+       # messy '\'; this drops all extra white space
+       out=$(echo ${out})
+       if [ "${out}" != "${expected}" ]; then
+               rc=1
+               if [ "${VERBOSE}" = "1" ]; then
+                       printf "    Unexpected route entry. Have:\n"
+                       printf "        ${out}\n"
+                       printf "    Expected:\n"
+                       printf "        ${expected}\n\n"
+               fi
+       fi
+
+       return $rc
+}
+
+route_cleanup()
+{
+       $IP li del red 2>/dev/null
+       $IP li del dummy1 2>/dev/null
+       $IP li del veth1 2>/dev/null
+       $IP li del veth3 2>/dev/null
+
+       cleanup &> /dev/null
+}
+
+route_setup()
+{
+       route_cleanup
+       setup
+
+       [ "${VERBOSE}" = "1" ] && set -x
+       set -e
+
+       $IP li add red up type vrf table 101
+       $IP li add veth1 type veth peer name veth2
+       $IP li add veth3 type veth peer name veth4
+
+       $IP li set veth1 up
+       $IP li set veth3 up
+       $IP li set veth2 vrf red up
+       $IP li set veth4 vrf red up
+       $IP li add dummy1 type dummy
+       $IP li set dummy1 vrf red up
+
+       $IP -6 addr add 2001:db8:101::1/64 dev veth1
+       $IP -6 addr add 2001:db8:101::2/64 dev veth2
+       $IP -6 addr add 2001:db8:103::1/64 dev veth3
+       $IP -6 addr add 2001:db8:103::2/64 dev veth4
+       $IP -6 addr add 2001:db8:104::1/64 dev dummy1
+
+       $IP addr add 172.16.101.1/24 dev veth1
+       $IP addr add 172.16.101.2/24 dev veth2
+       $IP addr add 172.16.103.1/24 dev veth3
+       $IP addr add 172.16.103.2/24 dev veth4
+       $IP addr add 172.16.104.1/24 dev dummy1
+
+       set +ex
+}
+
+# assumption is that basic add of a single path route works
+# otherwise just adding an address on an interface is broken
+ipv6_rt_add()
+{
+       local rc
+
+       echo
+       echo "IPv6 route add / append tests"
+
+       # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
+       add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::2"
+       log_test $? 2 "Attempt to add duplicate route - gw"
+
+       # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
+       add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro add 2001:db8:104::/64 dev veth3"
+       log_test $? 2 "Attempt to add duplicate route - dev only"
+
+       # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
+       add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro add unreachable 2001:db8:104::/64"
+       log_test $? 2 "Attempt to add duplicate route - reject route"
+
+       # iproute2 prepend only sets NLM_F_CREATE
+       # - adds a new route; does NOT convert existing route to ECMP
+       add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro prepend 2001:db8:104::/64 via 2001:db8:103::2"
+       check_route6 "2001:db8:104::/64 via 2001:db8:101::2 dev veth1 metric 1024 2001:db8:104::/64 via 2001:db8:103::2 dev veth3 metric 1024"
+       log_test $? 0 "Add new route for existing prefix (w/o NLM_F_EXCL)"
+
+       # route append with same prefix adds a new route
+       # - iproute2 sets NLM_F_CREATE | NLM_F_APPEND
+       add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro append 2001:db8:104::/64 via 2001:db8:103::2"
+       check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
+       log_test $? 0 "Append nexthop to existing route - gw"
+
+       add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro append 2001:db8:104::/64 dev veth3"
+       check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop dev veth3 weight 1"
+       log_test $? 0 "Append nexthop to existing route - dev only"
+
+       # multipath route can not have a nexthop that is a reject route
+       add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro append unreachable 2001:db8:104::/64"
+       log_test $? 2 "Append nexthop to existing route - reject route"
+
+       # reject route can not be converted to multipath route
+       run_cmd "$IP -6 ro flush 2001:db8:104::/64"
+       run_cmd "$IP -6 ro add unreachable 2001:db8:104::/64"
+       run_cmd "$IP -6 ro append 2001:db8:104::/64 via 2001:db8:103::2"
+       log_test $? 2 "Append nexthop to existing reject route - gw"
+
+       run_cmd "$IP -6 ro flush 2001:db8:104::/64"
+       run_cmd "$IP -6 ro add unreachable 2001:db8:104::/64"
+       run_cmd "$IP -6 ro append 2001:db8:104::/64 dev veth3"
+       log_test $? 2 "Append nexthop to existing reject route - dev only"
+
+       # insert mpath directly
+       add_route6 "2001:db8:104::/64" "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
+       check_route6  "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
+       log_test $? 0 "Add multipath route"
+
+       add_route6 "2001:db8:104::/64" "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
+       run_cmd "$IP -6 ro add 2001:db8:104::/64 nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
+       log_test $? 2 "Attempt to add duplicate multipath route"
+
+       # insert of a second route without append but different metric
+       add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::2 metric 512"
+       rc=$?
+       if [ $rc -eq 0 ]; then
+               run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::3 metric 256"
+               rc=$?
+       fi
+       log_test $rc 0 "Route add with different metrics"
+
+       run_cmd "$IP -6 ro del 2001:db8:104::/64 metric 512"
+       rc=$?
+       if [ $rc -eq 0 ]; then
+               check_route6 "2001:db8:104::/64 via 2001:db8:103::3 dev veth3 metric 256 2001:db8:104::/64 via 2001:db8:101::2 dev veth1 metric 1024"
+               rc=$?
+       fi
+       log_test $rc 0 "Route delete with metric"
+}
+
+ipv6_rt_replace_single()
+{
+       # single path with single path
+       #
+       add_initial_route6 "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:103::2"
+       check_route6 "2001:db8:104::/64 via 2001:db8:103::2 dev veth3 metric 1024"
+       log_test $? 0 "Single path with single path"
+
+       # single path with multipath
+       #
+       add_initial_route6 "nexthop via 2001:db8:101::2"
+       run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::2"
+       check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::3 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
+       log_test $? 0 "Single path with multipath"
+
+       # single path with reject
+       #
+       add_initial_route6 "nexthop via 2001:db8:101::2"
+       run_cmd "$IP -6 ro replace unreachable 2001:db8:104::/64"
+       check_route6 "unreachable 2001:db8:104::/64 dev lo metric 1024"
+       log_test $? 0 "Single path with reject route"
+
+       # single path with single path using MULTIPATH attribute
+       #
+       add_initial_route6 "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:103::2"
+       check_route6 "2001:db8:104::/64 via 2001:db8:103::2 dev veth3 metric 1024"
+       log_test $? 0 "Single path with single path via multipath attribute"
+
+       # route replace fails - invalid nexthop
+       add_initial_route6 "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:104::2"
+       if [ $? -eq 0 ]; then
+               # previous command is expected to fail so if it returns 0
+               # that means the test failed.
+               log_test 0 1 "Invalid nexthop"
+       else
+               check_route6 "2001:db8:104::/64 via 2001:db8:101::2 dev veth1 metric 1024"
+               log_test $? 0 "Invalid nexthop"
+       fi
+
+       # replace non-existent route
+       # - note use of change versus replace since ip adds NLM_F_CREATE
+       #   for replace
+       add_initial_route6 "via 2001:db8:101::2"
+       run_cmd "$IP -6 ro change 2001:db8:105::/64 via 2001:db8:101::2"
+       log_test $? 2 "Single path - replace of non-existent route"
+}
+
+ipv6_rt_replace_mpath()
+{
+       # multipath with multipath
+       add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
+       run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::3"
+       check_route6  "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::3 dev veth1 weight 1 nexthop via 2001:db8:103::3 dev veth3 weight 1"
+       log_test $? 0 "Multipath with multipath"
+
+       # multipath with single
+       add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
+       run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:101::3"
+       check_route6  "2001:db8:104::/64 via 2001:db8:101::3 dev veth1 metric 1024"
+       log_test $? 0 "Multipath with single path"
+
+       # multipath with single
+       add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
+       run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3"
+       check_route6 "2001:db8:104::/64 via 2001:db8:101::3 dev veth1 metric 1024"
+       log_test $? 0 "Multipath with single path via multipath attribute"
+
+       # multipath with reject
+       add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
+       run_cmd "$IP -6 ro replace unreachable 2001:db8:104::/64"
+       check_route6 "unreachable 2001:db8:104::/64 dev lo metric 1024"
+       log_test $? 0 "Multipath with reject route"
+
+       # route replace fails - invalid nexthop 1
+       add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
+       run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:111::3 nexthop via 2001:db8:103::3"
+       check_route6  "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
+       log_test $? 0 "Multipath - invalid first nexthop"
+
+       # route replace fails - invalid nexthop 2
+       add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
+       run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:113::3"
+       check_route6  "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
+       log_test $? 0 "Multipath - invalid second nexthop"
+
+       # multipath non-existent route
+       add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
+       run_cmd "$IP -6 ro change 2001:db8:105::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::3"
+       log_test $? 2 "Multipath - replace of non-existent route"
+}
+
+ipv6_rt_replace()
+{
+       echo
+       echo "IPv6 route replace tests"
+
+       ipv6_rt_replace_single
+       ipv6_rt_replace_mpath
+}
+
+ipv6_route_test()
+{
+       route_setup
+
+       ipv6_rt_add
+       ipv6_rt_replace
+
+       route_cleanup
+}
+
+# add route for a prefix, flushing any existing routes first
+# expected to be the first step of a test
+add_route()
+{
+       local pfx="$1"
+       local nh="$2"
+       local out
+
+       if [ "$VERBOSE" = "1" ]; then
+               echo
+               echo "    ##################################################"
+               echo
+       fi
+
+       run_cmd "$IP ro flush ${pfx}"
+       [ $? -ne 0 ] && exit 1
+
+       out=$($IP ro ls match ${pfx})
+       if [ -n "$out" ]; then
+               echo "Failed to flush routes for prefix used for tests."
+               exit 1
+       fi
+
+       run_cmd "$IP ro add ${pfx} ${nh}"
+       if [ $? -ne 0 ]; then
+               echo "Failed to add initial route for test."
+               exit 1
+       fi
+}
+
+# add initial route - used in replace route tests
+add_initial_route()
+{
+       add_route "172.16.104.0/24" "$1"
+}
+
+check_route()
+{
+       local pfx="172.16.104.0/24"
+       local expected="$1"
+       local out
+       local rc=0
+
+       out=$($IP ro ls match ${pfx})
+       if [ -z "${out}" ]; then
+               if [ "$VERBOSE" = "1" ]; then
+                       printf "\nNo route entry found\n"
+                       printf "Expected:\n"
+                       printf "    ${expected}\n"
+               fi
+               return 1
+       fi
+
+       # tricky way to convert output to 1-line without ip's
+       # messy '\'; this drops all extra white space
+       out=$(echo ${out})
+       if [ "${out}" != "${expected}" ]; then
+               rc=1
+               if [ "${VERBOSE}" = "1" ]; then
+                       printf "    Unexpected route entry. Have:\n"
+                       printf "        ${out}\n"
+                       printf "    Expected:\n"
+                       printf "        ${expected}\n\n"
+               fi
+       fi
+
+       return $rc
+}
+
+# assumption is that basic add of a single path route works
+# otherwise just adding an address on an interface is broken
+ipv4_rt_add()
+{
+       local rc
+
+       echo
+       echo "IPv4 route add / append tests"
+
+       # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
+       add_route "172.16.104.0/24" "via 172.16.101.2"
+       run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.2"
+       log_test $? 2 "Attempt to add duplicate route - gw"
+
+       # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
+       add_route "172.16.104.0/24" "via 172.16.101.2"
+       run_cmd "$IP ro add 172.16.104.0/24 dev veth3"
+       log_test $? 2 "Attempt to add duplicate route - dev only"
+
+       # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
+       add_route "172.16.104.0/24" "via 172.16.101.2"
+       run_cmd "$IP ro add unreachable 172.16.104.0/24"
+       log_test $? 2 "Attempt to add duplicate route - reject route"
+
+       # iproute2 prepend only sets NLM_F_CREATE
+       # - adds a new route; does NOT convert existing route to ECMP
+       add_route "172.16.104.0/24" "via 172.16.101.2"
+       run_cmd "$IP ro prepend 172.16.104.0/24 via 172.16.103.2"
+       check_route "172.16.104.0/24 via 172.16.103.2 dev veth3 172.16.104.0/24 via 172.16.101.2 dev veth1"
+       log_test $? 0 "Add new nexthop for existing prefix"
+
+       # route append with same prefix adds a new route
+       # - iproute2 sets NLM_F_CREATE | NLM_F_APPEND
+       add_route "172.16.104.0/24" "via 172.16.101.2"
+       run_cmd "$IP ro append 172.16.104.0/24 via 172.16.103.2"
+       check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 172.16.104.0/24 via 172.16.103.2 dev veth3"
+       log_test $? 0 "Append nexthop to existing route - gw"
+
+       add_route "172.16.104.0/24" "via 172.16.101.2"
+       run_cmd "$IP ro append 172.16.104.0/24 dev veth3"
+       check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 172.16.104.0/24 dev veth3 scope link"
+       log_test $? 0 "Append nexthop to existing route - dev only"
+
+       add_route "172.16.104.0/24" "via 172.16.101.2"
+       run_cmd "$IP ro append unreachable 172.16.104.0/24"
+       check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 unreachable 172.16.104.0/24"
+       log_test $? 0 "Append nexthop to existing route - reject route"
+
+       run_cmd "$IP ro flush 172.16.104.0/24"
+       run_cmd "$IP ro add unreachable 172.16.104.0/24"
+       run_cmd "$IP ro append 172.16.104.0/24 via 172.16.103.2"
+       check_route "unreachable 172.16.104.0/24 172.16.104.0/24 via 172.16.103.2 dev veth3"
+       log_test $? 0 "Append nexthop to existing reject route - gw"
+
+       run_cmd "$IP ro flush 172.16.104.0/24"
+       run_cmd "$IP ro add unreachable 172.16.104.0/24"
+       run_cmd "$IP ro append 172.16.104.0/24 dev veth3"
+       check_route "unreachable 172.16.104.0/24 172.16.104.0/24 dev veth3 scope link"
+       log_test $? 0 "Append nexthop to existing reject route - dev only"
+
+       # insert mpath directly
+       add_route "172.16.104.0/24" "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
+       check_route  "172.16.104.0/24 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
+       log_test $? 0 "add multipath route"
+
+       add_route "172.16.104.0/24" "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
+       run_cmd "$IP ro add 172.16.104.0/24 nexthop via 172.16.101.2 nexthop via 172.16.103.2"
+       log_test $? 2 "Attempt to add duplicate multipath route"
+
+       # insert of a second route without append but different metric
+       add_route "172.16.104.0/24" "via 172.16.101.2"
+       run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.2 metric 512"
+       rc=$?
+       if [ $rc -eq 0 ]; then
+               run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.3 metric 256"
+               rc=$?
+       fi
+       log_test $rc 0 "Route add with different metrics"
+
+       run_cmd "$IP ro del 172.16.104.0/24 metric 512"
+       rc=$?
+       if [ $rc -eq 0 ]; then
+               check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 172.16.104.0/24 via 172.16.103.3 dev veth3 metric 256"
+               rc=$?
+       fi
+       log_test $rc 0 "Route delete with metric"
+}
 
-fib_test()
+ipv4_rt_replace_single()
 {
-       if [ -n "$TEST" ]; then
-               eval $TEST
+       # single path with single path
+       #
+       add_initial_route "via 172.16.101.2"
+       run_cmd "$IP ro replace 172.16.104.0/24 via 172.16.103.2"
+       check_route "172.16.104.0/24 via 172.16.103.2 dev veth3"
+       log_test $? 0 "Single path with single path"
+
+       # single path with multipath
+       #
+       add_initial_route "nexthop via 172.16.101.2"
+       run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.2"
+       check_route "172.16.104.0/24 nexthop via 172.16.101.3 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
+       log_test $? 0 "Single path with multipath"
+
+       # single path with reject
+       #
+       add_initial_route "nexthop via 172.16.101.2"
+       run_cmd "$IP ro replace unreachable 172.16.104.0/24"
+       check_route "unreachable 172.16.104.0/24"
+       log_test $? 0 "Single path with reject route"
+
+       # single path with single path using MULTIPATH attribute
+       #
+       add_initial_route "via 172.16.101.2"
+       run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.103.2"
+       check_route "172.16.104.0/24 via 172.16.103.2 dev veth3"
+       log_test $? 0 "Single path with single path via multipath attribute"
+
+       # route replace fails - invalid nexthop
+       add_initial_route "via 172.16.101.2"
+       run_cmd "$IP ro replace 172.16.104.0/24 via 2001:db8:104::2"
+       if [ $? -eq 0 ]; then
+               # previous command is expected to fail so if it returns 0
+               # that means the test failed.
+               log_test 0 1 "Invalid nexthop"
        else
-               fib_unreg_test
-               fib_down_test
-               fib_carrier_test
-               fib_nexthop_test
+               check_route "172.16.104.0/24 via 172.16.101.2 dev veth1"
+               log_test $? 0 "Invalid nexthop"
        fi
+
+       # replace non-existent route
+       # - note use of change versus replace since ip adds NLM_F_CREATE
+       #   for replace
+       add_initial_route "via 172.16.101.2"
+       run_cmd "$IP ro change 172.16.105.0/24 via 172.16.101.2"
+       log_test $? 2 "Single path - replace of non-existent route"
+}
+
+ipv4_rt_replace_mpath()
+{
+       # multipath with multipath
+       add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
+       run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.3"
+       check_route  "172.16.104.0/24 nexthop via 172.16.101.3 dev veth1 weight 1 nexthop via 172.16.103.3 dev veth3 weight 1"
+       log_test $? 0 "Multipath with multipath"
+
+       # multipath with single
+       add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
+       run_cmd "$IP ro replace 172.16.104.0/24 via 172.16.101.3"
+       check_route  "172.16.104.0/24 via 172.16.101.3 dev veth1"
+       log_test $? 0 "Multipath with single path"
+
+       # multipath with single
+       add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
+       run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3"
+       check_route "172.16.104.0/24 via 172.16.101.3 dev veth1"
+       log_test $? 0 "Multipath with single path via multipath attribute"
+
+       # multipath with reject
+       add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
+       run_cmd "$IP ro replace unreachable 172.16.104.0/24"
+       check_route "unreachable 172.16.104.0/24"
+       log_test $? 0 "Multipath with reject route"
+
+       # route replace fails - invalid nexthop 1
+       add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
+       run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.111.3 nexthop via 172.16.103.3"
+       check_route  "172.16.104.0/24 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
+       log_test $? 0 "Multipath - invalid first nexthop"
+
+       # route replace fails - invalid nexthop 2
+       add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
+       run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.113.3"
+       check_route  "172.16.104.0/24 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
+       log_test $? 0 "Multipath - invalid second nexthop"
+
+       # multipath non-existent route
+       add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
+       run_cmd "$IP ro change 172.16.105.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.3"
+       log_test $? 2 "Multipath - replace of non-existent route"
+}
+
+ipv4_rt_replace()
+{
+       echo
+       echo "IPv4 route replace tests"
+
+       ipv4_rt_replace_single
+       ipv4_rt_replace_mpath
+}
+
+ipv4_route_test()
+{
+       route_setup
+
+       ipv4_rt_add
+       ipv4_rt_replace
+
+       route_cleanup
+}
+
+################################################################################
+# usage
+
+usage()
+{
+       cat <<EOF
+usage: ${0##*/} OPTS
+
+        -t <test>   Test(s) to run (default: all)
+                    (options: $TESTS)
+        -p          Pause on fail
+        -P          Pause after each test before cleanup
+        -v          verbose mode (show commands and output)
+EOF
 }
 
+################################################################################
+# main
+
+while getopts :t:pPhv o
+do
+       case $o in
+               t) TESTS=$OPTARG;;
+               p) PAUSE_ON_FAIL=yes;;
+               P) PAUSE=yes;;
+               v) VERBOSE=$(($VERBOSE + 1));;
+               h) usage; exit 0;;
+               *) usage; exit 1;;
+       esac
+done
+
+PEER_CMD="ip netns exec ${PEER_NS}"
+
+# make sure we don't pause twice
+[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
+
 if [ "$(id -u)" -ne 0 ];then
        echo "SKIP: Need root privileges"
        exit 0
@@ -596,6 +1236,23 @@ fi
 # start clean
 cleanup &> /dev/null
 
-fib_test
+for t in $TESTS
+do
+       case $t in
+       fib_unreg_test|unregister)      fib_unreg_test;;
+       fib_down_test|down)             fib_down_test;;
+       fib_carrier_test|carrier)       fib_carrier_test;;
+       fib_nexthop_test|nexthop)       fib_nexthop_test;;
+       ipv6_route_test|ipv6_rt)        ipv6_route_test;;
+       ipv4_route_test|ipv4_rt)        ipv4_route_test;;
+
+       help) echo "Test names: $TESTS"; exit 0;;
+       esac
+done
+
+if [ "$TESTS" != "none" ]; then
+       printf "\nTests passed: %3d\n" ${nsuccess}
+       printf "Tests failed: %3d\n"   ${nfail}
+fi
 
 exit $ret
index 0377773..de97e4f 100644 (file)
@@ -20,7 +20,7 @@
         "matchPattern": "action order [0-9]*: ife encode action pass.*type 0xED3E.*allow mark.*index 2",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
@@ -44,7 +44,7 @@
         "matchPattern": "action order [0-9]*: ife encode action pipe.*type 0xED3E.*use mark.*index 2",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
@@ -68,7 +68,7 @@
         "matchPattern": "action order [0-9]*: ife encode action continue.*type 0xED3E.*allow mark.*index 2",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
@@ -92,7 +92,7 @@
         "matchPattern": "action order [0-9]*: ife encode action drop.*type 0xED3E.*use mark 789.*index 2",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
         "matchPattern": "action order [0-9]*: ife encode action reclassify.*type 0xED3E.*use mark 656768.*index 2",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
         "matchPattern": "action order [0-9]*: ife encode action jump 1.*type 0xED3E.*use mark 65.*index 2",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
         "matchPattern": "action order [0-9]*: ife encode action reclassify.*type 0xED3E.*use mark 4294967295.*index 90",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
         "matchPattern": "action order [0-9]*: ife encode action pass.*type 0xED3E.*allow prio.*index 9",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
         "matchPattern": "action order [0-9]*: ife encode action pipe.*type 0xED3E.*use prio 7.*index 9",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
         "matchPattern": "action order [0-9]*: ife encode action continue.*type 0xED3E.*use prio 3.*index 9",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
         "matchPattern": "action order [0-9]*: ife encode action drop.*type 0xED3E.*allow prio.*index 9",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
         "matchPattern": "action order [0-9]*: ife encode action reclassify.*type 0xED3E.*use prio 998877.*index 9",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
         "matchPattern": "action order [0-9]*: ife encode action jump 10.*type 0xED3E.*use prio 998877.*index 9",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
         "matchPattern": "action order [0-9]*: ife encode action reclassify.*type 0xED3E.*use prio 4294967295.*index 99",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action skbedit"
+            "$TC actions flush action ife"
         ]
     },
     {
index 443c9b3..6e4edfa 100644 (file)
     },
     {
         "id": "8b69",
-        "name": "Add mirred mirror action with maximum index",
+        "name": "Add mirred mirror action with index at 32-bit maximum",
         "category": [
             "actions",
             "mirred"
             "$TC actions flush action mirred"
         ]
     },
+    {
+        "id": "3f66",
+        "name": "Add mirred mirror action with index exceeding 32-bit maximum",
+        "category": [
+            "actions",
+            "mirred"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action mirred",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo pipe index 429496729555",
+        "expExitCode": "255",
+        "verifyCmd": "$TC actions get action mirred index 429496729555",
+        "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) pipe.*index 429496729555",
+        "matchCount": "0",
+        "teardown": []
+    },
     {
         "id": "a70e",
         "name": "Delete mirred mirror action",
index 38d85a1..f03763d 100644 (file)
         ],
         "cmdUnderTest": "$TC actions add action police rate 10mbit burst 10k index 4294967295",
         "expExitCode": "0",
-        "verifyCmd": "$TC actions get action mirred index 4294967295",
+        "verifyCmd": "$TC actions get action police index 4294967295",
         "matchPattern": "action order [0-9]*:  police 0xffffffff rate 10Mbit burst 10Kb mtu 2Kb",
         "matchCount": "1",
         "teardown": [
-            "$TC actions flush action mirred"
+            "$TC actions flush action police"
         ]
     },
     {
index 4510ddf..69ea09e 100644 (file)
@@ -1,7 +1,7 @@
 [
     {
         "id": "6f5a",
-        "name": "Add vlan pop action",
+        "name": "Add vlan pop action with pipe opcode",
         "category": [
             "actions",
             "vlan"
                 255
             ]
         ],
-        "cmdUnderTest": "$TC actions add action vlan pop index 8",
+        "cmdUnderTest": "$TC actions add action vlan pop pipe index 8",
         "expExitCode": "0",
         "verifyCmd": "$TC actions list action vlan",
-        "matchPattern": "action order [0-9]+: vlan.*pop.*index 8 ref",
+        "matchPattern": "action order [0-9]+: vlan.*pop.*pipe.*index 8 ref",
         "matchCount": "1",
         "teardown": [
             "$TC actions flush action vlan"
         ]
     },
     {
-        "id": "ee6f",
-        "name": "Add vlan pop action with large index",
+        "id": "df35",
+        "name": "Add vlan pop action with pass opcode",
         "category": [
             "actions",
             "vlan"
                 255
             ]
         ],
-        "cmdUnderTest": "$TC actions add action vlan pop index 4294967295",
+        "cmdUnderTest": "$TC actions add action vlan pop pass index 8",
         "expExitCode": "0",
-        "verifyCmd": "$TC actions list action vlan",
-        "matchPattern": "action order [0-9]+: vlan.*pop.*index 4294967295 ref",
+        "verifyCmd": "$TC actions get action vlan index 8",
+        "matchPattern": "action order [0-9]+: vlan.*pop.*pass.*index 8 ref",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action vlan"
+        ]
+    },
+    {
+        "id": "b0d4",
+        "name": "Add vlan pop action with drop opcode",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions add action vlan pop drop index 8",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions get action vlan index 8",
+        "matchPattern": "action order [0-9]+: vlan.*pop.*drop.*index 8 ref",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action vlan"
+        ]
+    },
+    {
+        "id": "95ee",
+        "name": "Add vlan pop action with reclassify opcode",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions add action vlan pop reclassify index 8",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions get action vlan index 8",
+        "matchPattern": "action order [0-9]+: vlan.*pop.*reclassify.*index 8 ref",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action vlan"
+        ]
+    },
+    {
+        "id": "0283",
+        "name": "Add vlan pop action with continue opcode",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions add action vlan pop continue index 8",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions get action vlan index 8",
+        "matchPattern": "action order [0-9]+: vlan.*pop.*continue.*index 8 ref",
         "matchCount": "1",
         "teardown": [
             "$TC actions flush action vlan"
             "$TC actions flush action vlan"
         ]
     },
+    {
+        "id": "a178",
+        "name": "Add vlan pop action with invalid opcode",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions add action vlan pop foo index 8",
+        "expExitCode": "255",
+        "verifyCmd": "$TC actions list action vlan",
+        "matchPattern": "action order [0-9]+: vlan.*pop.*foo.*index 8 ref",
+        "matchCount": "0",
+        "teardown": []
+    },
+    {
+        "id": "ee6f",
+        "name": "Add vlan pop action with index at 32-bit maximum",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions add action vlan pop index 4294967295",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions list action vlan",
+        "matchPattern": "action order [0-9]+: vlan.*pop.*index 4294967295 ref",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action vlan"
+        ]
+    },
+    {
+        "id": "0dfa",
+        "name": "Add vlan pop action with index exceeding 32-bit maximum",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions add action vlan pop reclassify index 429496729599",
+        "expExitCode": "255",
+        "verifyCmd": "$TC actions get action vlan index 429496729599",
+        "matchPattern": "action order [0-9]+: vlan.*pop.reclassify.*index 429496729599",
+        "matchCount": "0",
+        "teardown": []
+    },
     {
         "id": "2b91",
         "name": "Add vlan invalid action",
         "verifyCmd": "$TC actions list action vlan",
         "matchPattern": "action order [0-9]+: vlan.*bad_mode",
         "matchCount": "0",
-        "teardown": [
-            "$TC actions flush action vlan"
-        ]
+        "teardown": []
     },
     {
         "id": "57fc",
-        "name": "Add vlan action with invalid protocol type",
+        "name": "Add vlan push action with invalid protocol type",
         "category": [
             "actions",
             "vlan"
         "verifyCmd": "$TC actions list action vlan",
         "matchPattern": "action order [0-9]+: vlan.*push",
         "matchCount": "0",
-        "teardown": [
-            "$TC actions flush action vlan"
-        ]
+        "teardown": []
     },
     {
         "id": "3989",
             "$TC actions flush action vlan"
         ]
     },
+    {
+        "id": "1f4b",
+        "name": "Add vlan push action with maximum 12-bit vlan ID",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions add action vlan push id 4094 index 1",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions get action vlan index 1",
+        "matchPattern": "action order [0-9]+: vlan.*push id 4094.*protocol 802.1Q.*priority 0.*index 1 ref",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action vlan"
+        ]
+    },
     {
         "id": "1f7b",
         "name": "Add vlan push action with invalid vlan ID",
             "$TC actions flush action vlan"
         ]
     },
+    {
+        "id": "fe40",
+        "name": "Add vlan push action with maximum 3-bit IEEE 802.1p priority",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions add action vlan push id 4 priority 7 reclassify index 1",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions get action vlan index 1",
+        "matchPattern": "action order [0-9]+: vlan.*push id 4.*protocol 802.1Q.*priority 7.*reclassify.*index 1 ref",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action vlan"
+        ]
+    },
     {
         "id": "5d02",
         "name": "Add vlan push action with invalid IEEE 802.1p priority",
         "verifyCmd": "$TC actions list action vlan",
         "matchPattern": "action order [0-9]+: vlan.*push id 5.*index 1 ref",
         "matchCount": "0",
-        "teardown": [
-            "$TC actions flush action vlan"
-        ]
+        "teardown": []
     },
     {
         "id": "6812",
             "$TC actions flush action vlan"
         ]
     },
+    {
+        "id": "3deb",
+        "name": "Replace existing vlan push action with new ID",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ],
+            "$TC actions add action vlan push id 500 pipe index 12"
+        ],
+        "cmdUnderTest": "$TC actions replace action vlan push id 700 pipe index 12",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions get action vlan index 12",
+        "matchPattern": "action order [0-9]+: vlan.*push id 700 protocol 802.1Q priority 0 pipe.*index 12 ref",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action vlan"
+        ]
+    },
+    {
+        "id": "9e76",
+        "name": "Replace existing vlan push action with new protocol",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ],
+            "$TC actions add action vlan push id 1 protocol 802.1Q pipe index 1"
+        ],
+        "cmdUnderTest": "$TC actions replace action vlan push id 1 protocol 802.1ad pipe index 1",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions get action vlan index 1",
+        "matchPattern": "action order [0-9]+: vlan.*push id 1 protocol 802.1ad priority 0 pipe.*index 1 ref",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action vlan"
+        ]
+    },
+    {
+        "id": "ede4",
+        "name": "Replace existing vlan push action with new priority",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ],
+            "$TC actions add action vlan push id 1 protocol 802.1Q priority 3 reclassify index 1"
+        ],
+        "cmdUnderTest": "$TC actions replace action vlan push id 1 priority 4 reclassify index 1",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions get action vlan index 1",
+        "matchPattern": "action order [0-9]+: vlan.*push id 1 protocol 802.1Q priority 4 reclassify.*index 1 ref",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action vlan"
+        ]
+    },
+    {
+        "id": "d413",
+        "name": "Replace existing vlan pop action with new cookie",
+        "category": [
+            "actions",
+            "vlan"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action vlan",
+                0,
+                1,
+                255
+            ],
+            "$TC actions add action vlan pop continue index 1 cookie 22334455"
+        ],
+        "cmdUnderTest": "$TC actions replace action vlan pop continue index 1 cookie a1b1c2d1",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions get action vlan index 1",
+        "matchPattern": "action order [0-9]+: vlan.*pop continue.*index 1 ref.*cookie a1b1c2d1",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action vlan"
+        ]
+    },
     {
         "id": "83a4",
         "name": "Delete vlan pop action",
     },
     {
         "id": "1d78",
-        "name": "Add vlan action with cookie",
+        "name": "Add vlan push action with cookie",
         "category": [
             "actions",
             "vlan"
diff --git a/tools/testing/selftests/uevent/Makefile b/tools/testing/selftests/uevent/Makefile
new file mode 100644 (file)
index 0000000..f7baa9a
--- /dev/null
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+all:
+
+include ../lib.mk
+
+.PHONY: all clean
+
+BINARIES := uevent_filtering
+CFLAGS += -Wl,-no-as-needed -Wall
+
+uevent_filtering: uevent_filtering.c ../kselftest.h ../kselftest_harness.h
+       $(CC) $(CFLAGS) $< -o $@
+
+TEST_PROGS += $(BINARIES)
+EXTRA_CLEAN := $(BINARIES)
+
+all: $(BINARIES)
diff --git a/tools/testing/selftests/uevent/config b/tools/testing/selftests/uevent/config
new file mode 100644 (file)
index 0000000..1038f45
--- /dev/null
@@ -0,0 +1,2 @@
+CONFIG_USER_NS=y
+CONFIG_NET=y
diff --git a/tools/testing/selftests/uevent/uevent_filtering.c b/tools/testing/selftests/uevent/uevent_filtering.c
new file mode 100644 (file)
index 0000000..f83391a
--- /dev/null
@@ -0,0 +1,486 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/netlink.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sched.h>
+#include <sys/eventfd.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../kselftest.h"
+#include "../kselftest_harness.h"
+
+#define __DEV_FULL "/sys/devices/virtual/mem/full/uevent"
+#define __UEVENT_BUFFER_SIZE (2048 * 2)
+#define __UEVENT_HEADER "add@/devices/virtual/mem/full"
+#define __UEVENT_HEADER_LEN sizeof("add@/devices/virtual/mem/full")
+#define __UEVENT_LISTEN_ALL -1
+
+ssize_t read_nointr(int fd, void *buf, size_t count)
+{
+       ssize_t ret;
+
+again:
+       ret = read(fd, buf, count);
+       if (ret < 0 && errno == EINTR)
+               goto again;
+
+       return ret;
+}
+
+ssize_t write_nointr(int fd, const void *buf, size_t count)
+{
+       ssize_t ret;
+
+again:
+       ret = write(fd, buf, count);
+       if (ret < 0 && errno == EINTR)
+               goto again;
+
+       return ret;
+}
+
+int wait_for_pid(pid_t pid)
+{
+       int status, ret;
+
+again:
+       ret = waitpid(pid, &status, 0);
+       if (ret == -1) {
+               if (errno == EINTR)
+                       goto again;
+
+               return -1;
+       }
+
+       if (ret != pid)
+               goto again;
+
+       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+               return -1;
+
+       return 0;
+}
+
+static int uevent_listener(unsigned long post_flags, bool expect_uevent,
+                          int sync_fd)
+{
+       int sk_fd, ret;
+       socklen_t sk_addr_len;
+       int fret = -1, rcv_buf_sz = __UEVENT_BUFFER_SIZE;
+       uint64_t sync_add = 1;
+       struct sockaddr_nl sk_addr = { 0 }, rcv_addr = { 0 };
+       char buf[__UEVENT_BUFFER_SIZE] = { 0 };
+       struct iovec iov = { buf, __UEVENT_BUFFER_SIZE };
+       char control[CMSG_SPACE(sizeof(struct ucred))];
+       struct msghdr hdr = {
+               &rcv_addr, sizeof(rcv_addr), &iov, 1,
+               control,   sizeof(control),  0,
+       };
+
+       sk_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC,
+                      NETLINK_KOBJECT_UEVENT);
+       if (sk_fd < 0) {
+               fprintf(stderr, "%s - Failed to open uevent socket\n", strerror(errno));
+               return -1;
+       }
+
+       ret = setsockopt(sk_fd, SOL_SOCKET, SO_RCVBUF, &rcv_buf_sz,
+                        sizeof(rcv_buf_sz));
+       if (ret < 0) {
+               fprintf(stderr, "%s - Failed to set socket options\n", strerror(errno));
+               goto on_error;
+       }
+
+       sk_addr.nl_family = AF_NETLINK;
+       sk_addr.nl_groups = __UEVENT_LISTEN_ALL;
+
+       sk_addr_len = sizeof(sk_addr);
+       ret = bind(sk_fd, (struct sockaddr *)&sk_addr, sk_addr_len);
+       if (ret < 0) {
+               fprintf(stderr, "%s - Failed to bind socket\n", strerror(errno));
+               goto on_error;
+       }
+
+       ret = getsockname(sk_fd, (struct sockaddr *)&sk_addr, &sk_addr_len);
+       if (ret < 0) {
+               fprintf(stderr, "%s - Failed to retrieve socket name\n", strerror(errno));
+               goto on_error;
+       }
+
+       if ((size_t)sk_addr_len != sizeof(sk_addr)) {
+               fprintf(stderr, "Invalid socket address size\n");
+               goto on_error;
+       }
+
+       if (post_flags & CLONE_NEWUSER) {
+               ret = unshare(CLONE_NEWUSER);
+               if (ret < 0) {
+                       fprintf(stderr,
+                               "%s - Failed to unshare user namespace\n",
+                               strerror(errno));
+                       goto on_error;
+               }
+       }
+
+       if (post_flags & CLONE_NEWNET) {
+               ret = unshare(CLONE_NEWNET);
+               if (ret < 0) {
+                       fprintf(stderr,
+                               "%s - Failed to unshare network namespace\n",
+                               strerror(errno));
+                       goto on_error;
+               }
+       }
+
+       ret = write_nointr(sync_fd, &sync_add, sizeof(sync_add));
+       close(sync_fd);
+       if (ret != sizeof(sync_add)) {
+               fprintf(stderr, "Failed to synchronize with parent process\n");
+               goto on_error;
+       }
+
+       fret = 0;
+       for (;;) {
+               ssize_t r;
+
+               r = recvmsg(sk_fd, &hdr, 0);
+               if (r <= 0) {
+                       fprintf(stderr, "%s - Failed to receive uevent\n", strerror(errno));
+                       ret = -1;
+                       break;
+               }
+
+               /* ignore libudev messages */
+               if (memcmp(buf, "libudev", 8) == 0)
+                       continue;
+
+               /* ignore uevents we didn't trigger */
+               if (memcmp(buf, __UEVENT_HEADER, __UEVENT_HEADER_LEN) != 0)
+                       continue;
+
+               if (!expect_uevent) {
+                       fprintf(stderr, "Received unexpected uevent:\n");
+                       ret = -1;
+               }
+
+               if (TH_LOG_ENABLED) {
+                       /* If logging is enabled dump the received uevent. */
+                       (void)write_nointr(STDERR_FILENO, buf, r);
+                       (void)write_nointr(STDERR_FILENO, "\n", 1);
+               }
+
+               break;
+       }
+
+on_error:
+       close(sk_fd);
+
+       return fret;
+}
+
+int trigger_uevent(unsigned int times)
+{
+       int fd, ret;
+       unsigned int i;
+
+       fd = open(__DEV_FULL, O_RDWR | O_CLOEXEC);
+       if (fd < 0) {
+               if (errno != ENOENT)
+                       return -EINVAL;
+
+               return -1;
+       }
+
+       for (i = 0; i < times; i++) {
+               ret = write_nointr(fd, "add\n", sizeof("add\n") - 1);
+               if (ret < 0) {
+                       fprintf(stderr, "Failed to trigger uevent\n");
+                       break;
+               }
+       }
+       close(fd);
+
+       return ret;
+}
+
+int set_death_signal(void)
+{
+       int ret;
+       pid_t ppid;
+
+       ret = prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+
+       /* Check whether we have been orphaned. */
+       ppid = getppid();
+       if (ppid == 1) {
+               pid_t self;
+
+               self = getpid();
+               ret = kill(self, SIGKILL);
+       }
+
+       if (ret < 0)
+               return -1;
+
+       return 0;
+}
+
+static int do_test(unsigned long pre_flags, unsigned long post_flags,
+                  bool expect_uevent, int sync_fd)
+{
+       int ret;
+       uint64_t wait_val;
+       pid_t pid;
+       sigset_t mask;
+       sigset_t orig_mask;
+       struct timespec timeout;
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGCHLD);
+
+       ret = sigprocmask(SIG_BLOCK, &mask, &orig_mask);
+       if (ret < 0) {
+               fprintf(stderr, "%s- Failed to block SIGCHLD\n", strerror(errno));
+               return -1;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               fprintf(stderr, "%s - Failed to fork() new process\n", strerror(errno));
+               return -1;
+       }
+
+       if (pid == 0) {
+               /* Make sure that we go away when our parent dies. */
+               ret = set_death_signal();
+               if (ret < 0) {
+                       fprintf(stderr, "Failed to set PR_SET_PDEATHSIG to SIGKILL\n");
+                       _exit(EXIT_FAILURE);
+               }
+
+               if (pre_flags & CLONE_NEWUSER) {
+                       ret = unshare(CLONE_NEWUSER);
+                       if (ret < 0) {
+                               fprintf(stderr,
+                                       "%s - Failed to unshare user namespace\n",
+                                       strerror(errno));
+                               _exit(EXIT_FAILURE);
+                       }
+               }
+
+               if (pre_flags & CLONE_NEWNET) {
+                       ret = unshare(CLONE_NEWNET);
+                       if (ret < 0) {
+                               fprintf(stderr,
+                                       "%s - Failed to unshare network namespace\n",
+                                       strerror(errno));
+                               _exit(EXIT_FAILURE);
+                       }
+               }
+
+               if (uevent_listener(post_flags, expect_uevent, sync_fd) < 0)
+                       _exit(EXIT_FAILURE);
+
+               _exit(EXIT_SUCCESS);
+       }
+
+       ret = read_nointr(sync_fd, &wait_val, sizeof(wait_val));
+       if (ret != sizeof(wait_val)) {
+               fprintf(stderr, "Failed to synchronize with child process\n");
+               _exit(EXIT_FAILURE);
+       }
+
+       /* Trigger 10 uevents to account for the case where the kernel might
+        * drop some.
+        */
+       ret = trigger_uevent(10);
+       if (ret < 0)
+               fprintf(stderr, "Failed triggering uevents\n");
+
+       /* Wait for 2 seconds before considering this failed. This should be
+        * plenty of time for the kernel to deliver the uevent even under heavy
+        * load.
+        */
+       timeout.tv_sec = 2;
+       timeout.tv_nsec = 0;
+
+again:
+       ret = sigtimedwait(&mask, NULL, &timeout);
+       if (ret < 0) {
+               if (errno == EINTR)
+                       goto again;
+
+               if (!expect_uevent)
+                       ret = kill(pid, SIGTERM); /* success */
+               else
+                       ret = kill(pid, SIGUSR1); /* error */
+               if (ret < 0)
+                       return -1;
+       }
+
+       ret = wait_for_pid(pid);
+       if (ret < 0)
+               return -1;
+
+       return ret;
+}
+
+static void signal_handler(int sig)
+{
+       if (sig == SIGTERM)
+               _exit(EXIT_SUCCESS);
+
+       _exit(EXIT_FAILURE);
+}
+
+TEST(uevent_filtering)
+{
+       int ret, sync_fd;
+       struct sigaction act;
+
+       if (geteuid()) {
+               TH_LOG("Uevent filtering tests require root privileges. Skipping test");
+               _exit(KSFT_SKIP);
+       }
+
+       ret = access(__DEV_FULL, F_OK);
+       EXPECT_EQ(0, ret) {
+               if (errno == ENOENT) {
+                       TH_LOG(__DEV_FULL " does not exist. Skipping test");
+                       _exit(KSFT_SKIP);
+               }
+
+               _exit(KSFT_FAIL);
+       }
+
+       act.sa_handler = signal_handler;
+       act.sa_flags = 0;
+       sigemptyset(&act.sa_mask);
+
+       ret = sigaction(SIGTERM, &act, NULL);
+       ASSERT_EQ(0, ret);
+
+       sync_fd = eventfd(0, EFD_CLOEXEC);
+       ASSERT_GE(sync_fd, 0);
+
+       /*
+        * Setup:
+        * - Open uevent listening socket in initial network namespace owned by
+        *   initial user namespace.
+        * - Trigger uevent in initial network namespace owned by initial user
+        *   namespace.
+        * Expected Result:
+        * - uevent listening socket receives uevent
+        */
+       ret = do_test(0, 0, true, sync_fd);
+       ASSERT_EQ(0, ret) {
+               goto do_cleanup;
+       }
+
+       /*
+        * Setup:
+        * - Open uevent listening socket in non-initial network namespace
+        *   owned by initial user namespace.
+        * - Trigger uevent in initial network namespace owned by initial user
+        *   namespace.
+        * Expected Result:
+        * - uevent listening socket receives uevent
+        */
+       ret = do_test(CLONE_NEWNET, 0, true, sync_fd);
+       ASSERT_EQ(0, ret) {
+               goto do_cleanup;
+       }
+
+       /*
+        * Setup:
+        * - unshare user namespace
+        * - Open uevent listening socket in initial network namespace
+        *   owned by initial user namespace.
+        * - Trigger uevent in initial network namespace owned by initial user
+        *   namespace.
+        * Expected Result:
+        * - uevent listening socket receives uevent
+        */
+       ret = do_test(CLONE_NEWUSER, 0, true, sync_fd);
+       ASSERT_EQ(0, ret) {
+               goto do_cleanup;
+       }
+
+       /*
+        * Setup:
+        * - Open uevent listening socket in non-initial network namespace
+        *   owned by non-initial user namespace.
+        * - Trigger uevent in initial network namespace owned by initial user
+        *   namespace.
+        * Expected Result:
+        * - uevent listening socket receives no uevent
+        */
+       ret = do_test(CLONE_NEWUSER | CLONE_NEWNET, 0, false, sync_fd);
+       ASSERT_EQ(0, ret) {
+               goto do_cleanup;
+       }
+
+       /*
+        * Setup:
+        * - Open uevent listening socket in initial network namespace
+        *   owned by initial user namespace.
+        * - unshare network namespace
+        * - Trigger uevent in initial network namespace owned by initial user
+        *   namespace.
+        * Expected Result:
+        * - uevent listening socket receives uevent
+        */
+       ret = do_test(0, CLONE_NEWNET, true, sync_fd);
+       ASSERT_EQ(0, ret) {
+               goto do_cleanup;
+       }
+
+       /*
+        * Setup:
+        * - Open uevent listening socket in initial network namespace
+        *   owned by initial user namespace.
+        * - unshare user namespace
+        * - Trigger uevent in initial network namespace owned by initial user
+        *   namespace.
+        * Expected Result:
+        * - uevent listening socket receives uevent
+        */
+       ret = do_test(0, CLONE_NEWUSER, true, sync_fd);
+       ASSERT_EQ(0, ret) {
+               goto do_cleanup;
+       }
+
+       /*
+        * Setup:
+        * - Open uevent listening socket in initial network namespace
+        *   owned by initial user namespace.
+        * - unshare user namespace
+        * - unshare network namespace
+        * - Trigger uevent in initial network namespace owned by initial user
+        *   namespace.
+        * Expected Result:
+        * - uevent listening socket receives uevent
+        */
+       ret = do_test(0, CLONE_NEWUSER | CLONE_NEWNET, true, sync_fd);
+       ASSERT_EQ(0, ret) {
+               goto do_cleanup;
+       }
+
+do_cleanup:
+       close(sync_fd);
+}
+
+TEST_HARNESS_MAIN
index d744991..39f66bc 100644 (file)
@@ -11,7 +11,7 @@ CAN_BUILD_X86_64 := $(shell ./check_cc.sh $(CC) trivial_64bit_program.c)
 
 TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \
                        check_initial_reg_state sigreturn iopl mpx-mini-test ioperm \
-                       protection_keys test_vdso test_vsyscall
+                       protection_keys test_vdso test_vsyscall mov_ss_trap
 TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \
                        test_FCMOV test_FCOMI test_FISTTP \
                        vdso_restorer
diff --git a/tools/testing/selftests/x86/mov_ss_trap.c b/tools/testing/selftests/x86/mov_ss_trap.c
new file mode 100644 (file)
index 0000000..3c3a022
--- /dev/null
@@ -0,0 +1,285 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mov_ss_trap.c: Exercise the bizarre side effects of a watchpoint on MOV SS
+ *
+ * This does MOV SS from a watchpointed address followed by various
+ * types of kernel entries.  A MOV SS that hits a watchpoint will queue
+ * up a #DB trap but will not actually deliver that trap.  The trap
+ * will be delivered after the next instruction instead.  The CPU's logic
+ * seems to be:
+ *
+ *  - Any fault: drop the pending #DB trap.
+ *  - INT $N, INT3, INTO, SYSCALL, SYSENTER: enter the kernel and then
+ *    deliver #DB.
+ *  - ICEBP: enter the kernel but do not deliver the watchpoint trap
+ *  - breakpoint: only one #DB is delivered (phew!)
+ *
+ * There are plenty of ways for a kernel to handle this incorrectly.  This
+ * test tries to exercise all the cases.
+ *
+ * This should mostly cover CVE-2018-1087 and CVE-2018-8897.
+ */
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/user.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <setjmp.h>
+#include <sys/prctl.h>
+
+#define X86_EFLAGS_RF (1UL << 16)
+
+#if __x86_64__
+# define REG_IP REG_RIP
+#else
+# define REG_IP REG_EIP
+#endif
+
+unsigned short ss;
+extern unsigned char breakpoint_insn[];
+sigjmp_buf jmpbuf;
+static unsigned char altstack_data[SIGSTKSZ];
+
+static void enable_watchpoint(void)
+{
+       pid_t parent = getpid();
+       int status;
+
+       pid_t child = fork();
+       if (child < 0)
+               err(1, "fork");
+
+       if (child) {
+               if (waitpid(child, &status, 0) != child)
+                       err(1, "waitpid for child");
+       } else {
+               unsigned long dr0, dr1, dr7;
+
+               dr0 = (unsigned long)&ss;
+               dr1 = (unsigned long)breakpoint_insn;
+               dr7 = ((1UL << 1) |     /* G0 */
+                      (3UL << 16) |    /* RW0 = read or write */
+                      (1UL << 18) |    /* LEN0 = 2 bytes */
+                      (1UL << 3));     /* G1, RW1 = insn */
+
+               if (ptrace(PTRACE_ATTACH, parent, NULL, NULL) != 0)
+                       err(1, "PTRACE_ATTACH");
+
+               if (waitpid(parent, &status, 0) != parent)
+                       err(1, "waitpid for child");
+
+               if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[0]), dr0) != 0)
+                       err(1, "PTRACE_POKEUSER DR0");
+
+               if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[1]), dr1) != 0)
+                       err(1, "PTRACE_POKEUSER DR1");
+
+               if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[7]), dr7) != 0)
+                       err(1, "PTRACE_POKEUSER DR7");
+
+               printf("\tDR0 = %lx, DR1 = %lx, DR7 = %lx\n", dr0, dr1, dr7);
+
+               if (ptrace(PTRACE_DETACH, parent, NULL, NULL) != 0)
+                       err(1, "PTRACE_DETACH");
+
+               exit(0);
+       }
+}
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+                      int flags)
+{
+       struct sigaction sa;
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_sigaction = handler;
+       sa.sa_flags = SA_SIGINFO | flags;
+       sigemptyset(&sa.sa_mask);
+       if (sigaction(sig, &sa, 0))
+               err(1, "sigaction");
+}
+
+static char const * const signames[] = {
+       [SIGSEGV] = "SIGSEGV",
+       [SIGBUS] = "SIBGUS",
+       [SIGTRAP] = "SIGTRAP",
+       [SIGILL] = "SIGILL",
+};
+
+static void sigtrap(int sig, siginfo_t *si, void *ctx_void)
+{
+       ucontext_t *ctx = ctx_void;
+
+       printf("\tGot SIGTRAP with RIP=%lx, EFLAGS.RF=%d\n",
+              (unsigned long)ctx->uc_mcontext.gregs[REG_IP],
+              !!(ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_RF));
+}
+
+static void handle_and_return(int sig, siginfo_t *si, void *ctx_void)
+{
+       ucontext_t *ctx = ctx_void;
+
+       printf("\tGot %s with RIP=%lx\n", signames[sig],
+              (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
+}
+
+static void handle_and_longjmp(int sig, siginfo_t *si, void *ctx_void)
+{
+       ucontext_t *ctx = ctx_void;
+
+       printf("\tGot %s with RIP=%lx\n", signames[sig],
+              (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
+
+       siglongjmp(jmpbuf, 1);
+}
+
+int main()
+{
+       unsigned long nr;
+
+       asm volatile ("mov %%ss, %[ss]" : [ss] "=m" (ss));
+       printf("\tSS = 0x%hx, &SS = 0x%p\n", ss, &ss);
+
+       if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) == 0)
+               printf("\tPR_SET_PTRACER_ANY succeeded\n");
+
+       printf("\tSet up a watchpoint\n");
+       sethandler(SIGTRAP, sigtrap, 0);
+       enable_watchpoint();
+
+       printf("[RUN]\tRead from watched memory (should get SIGTRAP)\n");
+       asm volatile ("mov %[ss], %[tmp]" : [tmp] "=r" (nr) : [ss] "m" (ss));
+
+       printf("[RUN]\tMOV SS; INT3\n");
+       asm volatile ("mov %[ss], %%ss; int3" :: [ss] "m" (ss));
+
+       printf("[RUN]\tMOV SS; INT 3\n");
+       asm volatile ("mov %[ss], %%ss; .byte 0xcd, 0x3" :: [ss] "m" (ss));
+
+       printf("[RUN]\tMOV SS; CS CS INT3\n");
+       asm volatile ("mov %[ss], %%ss; .byte 0x2e, 0x2e; int3" :: [ss] "m" (ss));
+
+       printf("[RUN]\tMOV SS; CSx14 INT3\n");
+       asm volatile ("mov %[ss], %%ss; .fill 14,1,0x2e; int3" :: [ss] "m" (ss));
+
+       printf("[RUN]\tMOV SS; INT 4\n");
+       sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);
+       asm volatile ("mov %[ss], %%ss; int $4" :: [ss] "m" (ss));
+
+#ifdef __i386__
+       printf("[RUN]\tMOV SS; INTO\n");
+       sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);
+       nr = -1;
+       asm volatile ("add $1, %[tmp]; mov %[ss], %%ss; into"
+                     : [tmp] "+r" (nr) : [ss] "m" (ss));
+#endif
+
+       if (sigsetjmp(jmpbuf, 1) == 0) {
+               printf("[RUN]\tMOV SS; ICEBP\n");
+
+               /* Some emulators (e.g. QEMU TCG) don't emulate ICEBP. */
+               sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);
+
+               asm volatile ("mov %[ss], %%ss; .byte 0xf1" :: [ss] "m" (ss));
+       }
+
+       if (sigsetjmp(jmpbuf, 1) == 0) {
+               printf("[RUN]\tMOV SS; CLI\n");
+               sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
+               asm volatile ("mov %[ss], %%ss; cli" :: [ss] "m" (ss));
+       }
+
+       if (sigsetjmp(jmpbuf, 1) == 0) {
+               printf("[RUN]\tMOV SS; #PF\n");
+               sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
+               asm volatile ("mov %[ss], %%ss; mov (-1), %[tmp]"
+                             : [tmp] "=r" (nr) : [ss] "m" (ss));
+       }
+
+       /*
+        * INT $1: if #DB has DPL=3 and there isn't special handling,
+        * then the kernel will die.
+        */
+       if (sigsetjmp(jmpbuf, 1) == 0) {
+               printf("[RUN]\tMOV SS; INT 1\n");
+               sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
+               asm volatile ("mov %[ss], %%ss; int $1" :: [ss] "m" (ss));
+       }
+
+#ifdef __x86_64__
+       /*
+        * In principle, we should test 32-bit SYSCALL as well, but
+        * the calling convention is so unpredictable that it's
+        * not obviously worth the effort.
+        */
+       if (sigsetjmp(jmpbuf, 1) == 0) {
+               printf("[RUN]\tMOV SS; SYSCALL\n");
+               sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);
+               nr = SYS_getpid;
+               /*
+                * Toggle the high bit of RSP to make it noncanonical to
+                * strengthen this test on non-SMAP systems.
+                */
+               asm volatile ("btc $63, %%rsp\n\t"
+                             "mov %[ss], %%ss; syscall\n\t"
+                             "btc $63, %%rsp"
+                             : "+a" (nr) : [ss] "m" (ss)
+                             : "rcx"
+#ifdef __x86_64__
+                               , "r11"
+#endif
+                       );
+       }
+#endif
+
+       printf("[RUN]\tMOV SS; breakpointed NOP\n");
+       asm volatile ("mov %[ss], %%ss; breakpoint_insn: nop" :: [ss] "m" (ss));
+
+       /*
+        * Invoking SYSENTER directly breaks all the rules.  Just handle
+        * the SIGSEGV.
+        */
+       if (sigsetjmp(jmpbuf, 1) == 0) {
+               printf("[RUN]\tMOV SS; SYSENTER\n");
+               stack_t stack = {
+                       .ss_sp = altstack_data,
+                       .ss_size = SIGSTKSZ,
+               };
+               if (sigaltstack(&stack, NULL) != 0)
+                       err(1, "sigaltstack");
+               sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND | SA_ONSTACK);
+               nr = SYS_getpid;
+               asm volatile ("mov %[ss], %%ss; SYSENTER" : "+a" (nr)
+                             : [ss] "m" (ss) : "flags", "rcx"
+#ifdef __x86_64__
+                               , "r11"
+#endif
+                       );
+
+               /* We're unreachable here.  SYSENTER forgets RIP. */
+       }
+
+       if (sigsetjmp(jmpbuf, 1) == 0) {
+               printf("[RUN]\tMOV SS; INT $0x80\n");
+               sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
+               nr = 20;        /* compat getpid */
+               asm volatile ("mov %[ss], %%ss; int $0x80"
+                             : "+a" (nr) : [ss] "m" (ss)
+                             : "flags"
+#ifdef __x86_64__
+                               , "r8", "r9", "r10", "r11"
+#endif
+                       );
+       }
+
+       printf("[OK]\tI aten't dead\n");
+       return 0;
+}
index 9c0325e..50f7e92 100644 (file)
@@ -368,6 +368,11 @@ static int expected_bnd_index = -1;
 uint64_t shadow_plb[NR_MPX_BOUNDS_REGISTERS][2]; /* shadow MPX bound registers */
 unsigned long shadow_map[NR_MPX_BOUNDS_REGISTERS];
 
+/* Failed address bound checks: */
+#ifndef SEGV_BNDERR
+# define SEGV_BNDERR   3
+#endif
+
 /*
  * The kernel is supposed to provide some information about the bounds
  * exception in the siginfo.  It should match what we have in the bounds
@@ -419,8 +424,6 @@ void handler(int signum, siginfo_t *si, void *vucontext)
                br_count++;
                dprintf1("#BR 0x%jx (total seen: %d)\n", status, br_count);
 
-#define SEGV_BNDERR     3  /* failed address bound checks */
-
                dprintf2("Saw a #BR! status 0x%jx at %016lx br_reason: %jx\n",
                                status, ip, br_reason);
                dprintf2("si_signo: %d\n", si->si_signo);
index b3cb767..254e543 100644 (file)
@@ -26,30 +26,26 @@ static inline void sigsafe_printf(const char *format, ...)
 {
        va_list ap;
 
-       va_start(ap, format);
        if (!dprint_in_signal) {
+               va_start(ap, format);
                vprintf(format, ap);
+               va_end(ap);
        } else {
                int ret;
-               int len = vsnprintf(dprint_in_signal_buffer,
-                                   DPRINT_IN_SIGNAL_BUF_SIZE,
-                                   format, ap);
                /*
-                * len is amount that would have been printed,
-                * but actual write is truncated at BUF_SIZE.
+                * No printf() functions are signal-safe.
+                * They deadlock easily. Write the format
+                * string to get some output, even if
+                * incomplete.
                 */
-               if (len > DPRINT_IN_SIGNAL_BUF_SIZE)
-                       len = DPRINT_IN_SIGNAL_BUF_SIZE;
-               ret = write(1, dprint_in_signal_buffer, len);
+               ret = write(1, format, strlen(format));
                if (ret < 0)
-                       abort();
+                       exit(1);
        }
-       va_end(ap);
 }
 #define dprintf_level(level, args...) do {     \
        if (level <= DEBUG_LEVEL)               \
                sigsafe_printf(args);           \
-       fflush(NULL);                           \
 } while (0)
 #define dprintf0(args...) dprintf_level(0, args)
 #define dprintf1(args...) dprintf_level(1, args)
index f15aa5a..460b4bd 100644 (file)
@@ -72,10 +72,9 @@ extern void abort_hooks(void);
                                test_nr, iteration_nr); \
                dprintf0("errno at assert: %d", errno); \
                abort_hooks();                  \
-               assert(condition);              \
+               exit(__LINE__);                 \
        }                                       \
 } while (0)
-#define raw_assert(cond) assert(cond)
 
 void cat_into_file(char *str, char *file)
 {
@@ -87,12 +86,17 @@ void cat_into_file(char *str, char *file)
         * these need to be raw because they are called under
         * pkey_assert()
         */
-       raw_assert(fd >= 0);
+       if (fd < 0) {
+               fprintf(stderr, "error opening '%s'\n", str);
+               perror("error: ");
+               exit(__LINE__);
+       }
+
        ret = write(fd, str, strlen(str));
        if (ret != strlen(str)) {
                perror("write to file failed");
                fprintf(stderr, "filename: '%s' str: '%s'\n", file, str);
-               raw_assert(0);
+               exit(__LINE__);
        }
        close(fd);
 }
@@ -191,26 +195,30 @@ void lots_o_noops_around_write(int *write_to_me)
 #ifdef __i386__
 
 #ifndef SYS_mprotect_key
-# define SYS_mprotect_key 380
+# define SYS_mprotect_key      380
 #endif
+
 #ifndef SYS_pkey_alloc
-# define SYS_pkey_alloc         381
-# define SYS_pkey_free  382
+# define SYS_pkey_alloc                381
+# define SYS_pkey_free         382
 #endif
-#define REG_IP_IDX REG_EIP
-#define si_pkey_offset 0x14
+
+#define REG_IP_IDX             REG_EIP
+#define si_pkey_offset         0x14
 
 #else
 
 #ifndef SYS_mprotect_key
-# define SYS_mprotect_key 329
+# define SYS_mprotect_key      329
 #endif
+
 #ifndef SYS_pkey_alloc
-# define SYS_pkey_alloc         330
-# define SYS_pkey_free  331
+# define SYS_pkey_alloc                330
+# define SYS_pkey_free         331
 #endif
-#define REG_IP_IDX REG_RIP
-#define si_pkey_offset 0x20
+
+#define REG_IP_IDX             REG_RIP
+#define si_pkey_offset         0x20
 
 #endif
 
@@ -225,8 +233,14 @@ void dump_mem(void *dumpme, int len_bytes)
        }
 }
 
-#define SEGV_BNDERR     3  /* failed address bound checks */
-#define SEGV_PKUERR     4
+/* Failed address bound checks: */
+#ifndef SEGV_BNDERR
+# define SEGV_BNDERR           3
+#endif
+
+#ifndef SEGV_PKUERR
+# define SEGV_PKUERR           4
+#endif
 
 static char *si_code_str(int si_code)
 {
@@ -289,13 +303,6 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext)
                dump_mem(pkru_ptr - 128, 256);
        pkey_assert(*pkru_ptr);
 
-       si_pkey_ptr = (u32 *)(((u8 *)si) + si_pkey_offset);
-       dprintf1("si_pkey_ptr: %p\n", si_pkey_ptr);
-       dump_mem(si_pkey_ptr - 8, 24);
-       siginfo_pkey = *si_pkey_ptr;
-       pkey_assert(siginfo_pkey < NR_PKEYS);
-       last_si_pkey = siginfo_pkey;
-
        if ((si->si_code == SEGV_MAPERR) ||
            (si->si_code == SEGV_ACCERR) ||
            (si->si_code == SEGV_BNDERR)) {
@@ -303,6 +310,13 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext)
                exit(4);
        }
 
+       si_pkey_ptr = (u32 *)(((u8 *)si) + si_pkey_offset);
+       dprintf1("si_pkey_ptr: %p\n", si_pkey_ptr);
+       dump_mem((u8 *)si_pkey_ptr - 8, 24);
+       siginfo_pkey = *si_pkey_ptr;
+       pkey_assert(siginfo_pkey < NR_PKEYS);
+       last_si_pkey = siginfo_pkey;
+
        dprintf1("signal pkru from xsave: %08x\n", *pkru_ptr);
        /* need __rdpkru() version so we do not do shadow_pkru checking */
        dprintf1("signal pkru from  pkru: %08x\n", __rdpkru());
@@ -311,22 +325,6 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext)
        dprintf1("WARNING: set PRKU=0 to allow faulting instruction to continue\n");
        pkru_faults++;
        dprintf1("<<<<==================================================\n");
-       return;
-       if (trapno == 14) {
-               fprintf(stderr,
-                       "ERROR: In signal handler, page fault, trapno = %d, ip = %016lx\n",
-                       trapno, ip);
-               fprintf(stderr, "si_addr %p\n", si->si_addr);
-               fprintf(stderr, "REG_ERR: %lx\n",
-                               (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]);
-               exit(1);
-       } else {
-               fprintf(stderr, "unexpected trap %d! at 0x%lx\n", trapno, ip);
-               fprintf(stderr, "si_addr %p\n", si->si_addr);
-               fprintf(stderr, "REG_ERR: %lx\n",
-                               (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]);
-               exit(2);
-       }
        dprint_in_signal = 0;
 }
 
@@ -393,10 +391,15 @@ pid_t fork_lazy_child(void)
        return forkret;
 }
 
-#define PKEY_DISABLE_ACCESS    0x1
-#define PKEY_DISABLE_WRITE     0x2
+#ifndef PKEY_DISABLE_ACCESS
+# define PKEY_DISABLE_ACCESS   0x1
+#endif
+
+#ifndef PKEY_DISABLE_WRITE
+# define PKEY_DISABLE_WRITE    0x2
+#endif
 
-u32 pkey_get(int pkey, unsigned long flags)
+static u32 hw_pkey_get(int pkey, unsigned long flags)
 {
        u32 mask = (PKEY_DISABLE_ACCESS|PKEY_DISABLE_WRITE);
        u32 pkru = __rdpkru();
@@ -418,7 +421,7 @@ u32 pkey_get(int pkey, unsigned long flags)
        return masked_pkru;
 }
 
-int pkey_set(int pkey, unsigned long rights, unsigned long flags)
+static int hw_pkey_set(int pkey, unsigned long rights, unsigned long flags)
 {
        u32 mask = (PKEY_DISABLE_ACCESS|PKEY_DISABLE_WRITE);
        u32 old_pkru = __rdpkru();
@@ -452,15 +455,15 @@ void pkey_disable_set(int pkey, int flags)
                pkey, flags);
        pkey_assert(flags & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
 
-       pkey_rights = pkey_get(pkey, syscall_flags);
+       pkey_rights = hw_pkey_get(pkey, syscall_flags);
 
-       dprintf1("%s(%d) pkey_get(%d): %x\n", __func__,
+       dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
                        pkey, pkey, pkey_rights);
        pkey_assert(pkey_rights >= 0);
 
        pkey_rights |= flags;
 
-       ret = pkey_set(pkey, pkey_rights, syscall_flags);
+       ret = hw_pkey_set(pkey, pkey_rights, syscall_flags);
        assert(!ret);
        /*pkru and flags have the same format */
        shadow_pkru |= flags << (pkey * 2);
@@ -468,8 +471,8 @@ void pkey_disable_set(int pkey, int flags)
 
        pkey_assert(ret >= 0);
 
-       pkey_rights = pkey_get(pkey, syscall_flags);
-       dprintf1("%s(%d) pkey_get(%d): %x\n", __func__,
+       pkey_rights = hw_pkey_get(pkey, syscall_flags);
+       dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
                        pkey, pkey, pkey_rights);
 
        dprintf1("%s(%d) pkru: 0x%x\n", __func__, pkey, rdpkru());
@@ -483,24 +486,24 @@ void pkey_disable_clear(int pkey, int flags)
 {
        unsigned long syscall_flags = 0;
        int ret;
-       int pkey_rights = pkey_get(pkey, syscall_flags);
+       int pkey_rights = hw_pkey_get(pkey, syscall_flags);
        u32 orig_pkru = rdpkru();
 
        pkey_assert(flags & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
 
-       dprintf1("%s(%d) pkey_get(%d): %x\n", __func__,
+       dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
                        pkey, pkey, pkey_rights);
        pkey_assert(pkey_rights >= 0);
 
        pkey_rights |= flags;
 
-       ret = pkey_set(pkey, pkey_rights, 0);
+       ret = hw_pkey_set(pkey, pkey_rights, 0);
        /* pkru and flags have the same format */
        shadow_pkru &= ~(flags << (pkey * 2));
        pkey_assert(ret >= 0);
 
-       pkey_rights = pkey_get(pkey, syscall_flags);
-       dprintf1("%s(%d) pkey_get(%d): %x\n", __func__,
+       pkey_rights = hw_pkey_get(pkey, syscall_flags);
+       dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
                        pkey, pkey, pkey_rights);
 
        dprintf1("%s(%d) pkru: 0x%x\n", __func__, pkey, rdpkru());
@@ -674,10 +677,12 @@ int mprotect_pkey(void *ptr, size_t size, unsigned long orig_prot,
 struct pkey_malloc_record {
        void *ptr;
        long size;
+       int prot;
 };
 struct pkey_malloc_record *pkey_malloc_records;
+struct pkey_malloc_record *pkey_last_malloc_record;
 long nr_pkey_malloc_records;
-void record_pkey_malloc(void *ptr, long size)
+void record_pkey_malloc(void *ptr, long size, int prot)
 {
        long i;
        struct pkey_malloc_record *rec = NULL;
@@ -709,6 +714,8 @@ void record_pkey_malloc(void *ptr, long size)
                (int)(rec - pkey_malloc_records), rec, ptr, size);
        rec->ptr = ptr;
        rec->size = size;
+       rec->prot = prot;
+       pkey_last_malloc_record = rec;
        nr_pkey_malloc_records++;
 }
 
@@ -753,7 +760,7 @@ void *malloc_pkey_with_mprotect(long size, int prot, u16 pkey)
        pkey_assert(ptr != (void *)-1);
        ret = mprotect_pkey((void *)ptr, PAGE_SIZE, prot, pkey);
        pkey_assert(!ret);
-       record_pkey_malloc(ptr, size);
+       record_pkey_malloc(ptr, size, prot);
        rdpkru();
 
        dprintf1("%s() for pkey %d @ %p\n", __func__, pkey, ptr);
@@ -774,7 +781,7 @@ void *malloc_pkey_anon_huge(long size, int prot, u16 pkey)
        size = ALIGN_UP(size, HPAGE_SIZE * 2);
        ptr = mmap(NULL, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
        pkey_assert(ptr != (void *)-1);
-       record_pkey_malloc(ptr, size);
+       record_pkey_malloc(ptr, size, prot);
        mprotect_pkey(ptr, size, prot, pkey);
 
        dprintf1("unaligned ptr: %p\n", ptr);
@@ -847,7 +854,7 @@ void *malloc_pkey_hugetlb(long size, int prot, u16 pkey)
        pkey_assert(ptr != (void *)-1);
        mprotect_pkey(ptr, size, prot, pkey);
 
-       record_pkey_malloc(ptr, size);
+       record_pkey_malloc(ptr, size, prot);
 
        dprintf1("mmap()'d hugetlbfs for pkey %d @ %p\n", pkey, ptr);
        return ptr;
@@ -869,7 +876,7 @@ void *malloc_pkey_mmap_dax(long size, int prot, u16 pkey)
 
        mprotect_pkey(ptr, size, prot, pkey);
 
-       record_pkey_malloc(ptr, size);
+       record_pkey_malloc(ptr, size, prot);
 
        dprintf1("mmap()'d for pkey %d @ %p\n", pkey, ptr);
        close(fd);
@@ -918,13 +925,21 @@ void *malloc_pkey(long size, int prot, u16 pkey)
 }
 
 int last_pkru_faults;
+#define UNKNOWN_PKEY -2
 void expected_pk_fault(int pkey)
 {
        dprintf2("%s(): last_pkru_faults: %d pkru_faults: %d\n",
                        __func__, last_pkru_faults, pkru_faults);
        dprintf2("%s(%d): last_si_pkey: %d\n", __func__, pkey, last_si_pkey);
        pkey_assert(last_pkru_faults + 1 == pkru_faults);
-       pkey_assert(last_si_pkey == pkey);
+
+       /*
+       * For exec-only memory, we do not know the pkey in
+       * advance, so skip this check.
+       */
+       if (pkey != UNKNOWN_PKEY)
+               pkey_assert(last_si_pkey == pkey);
+
        /*
         * The signal handler shold have cleared out PKRU to let the
         * test program continue.  We now have to restore it.
@@ -939,10 +954,11 @@ void expected_pk_fault(int pkey)
        last_si_pkey = -1;
 }
 
-void do_not_expect_pk_fault(void)
-{
-       pkey_assert(last_pkru_faults == pkru_faults);
-}
+#define do_not_expect_pk_fault(msg)    do {                    \
+       if (last_pkru_faults != pkru_faults)                    \
+               dprintf0("unexpected PK fault: %s\n", msg);     \
+       pkey_assert(last_pkru_faults == pkru_faults);           \
+} while (0)
 
 int test_fds[10] = { -1 };
 int nr_test_fds;
@@ -1151,12 +1167,15 @@ void test_pkey_alloc_exhaust(int *ptr, u16 pkey)
        pkey_assert(i < NR_PKEYS*2);
 
        /*
-        * There are 16 pkeys supported in hardware.  One is taken
-        * up for the default (0) and another can be taken up by
-        * an execute-only mapping.  Ensure that we can allocate
-        * at least 14 (16-2).
+        * There are 16 pkeys supported in hardware.  Three are
+        * allocated by the time we get here:
+        *   1. The default key (0)
+        *   2. One possibly consumed by an execute-only mapping.
+        *   3. One allocated by the test code and passed in via
+        *      'pkey' to this function.
+        * Ensure that we can allocate at least another 13 (16-3).
         */
-       pkey_assert(i >= NR_PKEYS-2);
+       pkey_assert(i >= NR_PKEYS-3);
 
        for (i = 0; i < nr_allocated_pkeys; i++) {
                err = sys_pkey_free(allocated_pkeys[i]);
@@ -1165,6 +1184,35 @@ void test_pkey_alloc_exhaust(int *ptr, u16 pkey)
        }
 }
 
+/*
+ * pkey 0 is special.  It is allocated by default, so you do not
+ * have to call pkey_alloc() to use it first.  Make sure that it
+ * is usable.
+ */
+void test_mprotect_with_pkey_0(int *ptr, u16 pkey)
+{
+       long size;
+       int prot;
+
+       assert(pkey_last_malloc_record);
+       size = pkey_last_malloc_record->size;
+       /*
+        * This is a bit of a hack.  But mprotect() requires
+        * huge-page-aligned sizes when operating on hugetlbfs.
+        * So, make sure that we use something that's a multiple
+        * of a huge page when we can.
+        */
+       if (size >= HPAGE_SIZE)
+               size = HPAGE_SIZE;
+       prot = pkey_last_malloc_record->prot;
+
+       /* Use pkey 0 */
+       mprotect_pkey(ptr, size, prot, 0);
+
+       /* Make sure that we can set it back to the original pkey. */
+       mprotect_pkey(ptr, size, prot, pkey);
+}
+
 void test_ptrace_of_child(int *ptr, u16 pkey)
 {
        __attribute__((__unused__)) int peek_result;
@@ -1228,7 +1276,7 @@ void test_ptrace_of_child(int *ptr, u16 pkey)
        pkey_assert(ret != -1);
        /* Now access from the current task, and expect NO exception: */
        peek_result = read_ptr(plain_ptr);
-       do_not_expect_pk_fault();
+       do_not_expect_pk_fault("read plain pointer after ptrace");
 
        ret = ptrace(PTRACE_DETACH, child_pid, ignored, 0);
        pkey_assert(ret != -1);
@@ -1241,12 +1289,9 @@ void test_ptrace_of_child(int *ptr, u16 pkey)
        free(plain_ptr_unaligned);
 }
 
-void test_executing_on_unreadable_memory(int *ptr, u16 pkey)
+void *get_pointer_to_instructions(void)
 {
        void *p1;
-       int scratch;
-       int ptr_contents;
-       int ret;
 
        p1 = ALIGN_PTR_UP(&lots_o_noops_around_write, PAGE_SIZE);
        dprintf3("&lots_o_noops: %p\n", &lots_o_noops_around_write);
@@ -1256,7 +1301,23 @@ void test_executing_on_unreadable_memory(int *ptr, u16 pkey)
        /* Point 'p1' at the *second* page of the function: */
        p1 += PAGE_SIZE;
 
+       /*
+        * Try to ensure we fault this in on next touch to ensure
+        * we get an instruction fault as opposed to a data one
+        */
        madvise(p1, PAGE_SIZE, MADV_DONTNEED);
+
+       return p1;
+}
+
+void test_executing_on_unreadable_memory(int *ptr, u16 pkey)
+{
+       void *p1;
+       int scratch;
+       int ptr_contents;
+       int ret;
+
+       p1 = get_pointer_to_instructions();
        lots_o_noops_around_write(&scratch);
        ptr_contents = read_ptr(p1);
        dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents);
@@ -1272,12 +1333,55 @@ void test_executing_on_unreadable_memory(int *ptr, u16 pkey)
         */
        madvise(p1, PAGE_SIZE, MADV_DONTNEED);
        lots_o_noops_around_write(&scratch);
-       do_not_expect_pk_fault();
+       do_not_expect_pk_fault("executing on PROT_EXEC memory");
        ptr_contents = read_ptr(p1);
        dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents);
        expected_pk_fault(pkey);
 }
 
+void test_implicit_mprotect_exec_only_memory(int *ptr, u16 pkey)
+{
+       void *p1;
+       int scratch;
+       int ptr_contents;
+       int ret;
+
+       dprintf1("%s() start\n", __func__);
+
+       p1 = get_pointer_to_instructions();
+       lots_o_noops_around_write(&scratch);
+       ptr_contents = read_ptr(p1);
+       dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents);
+
+       /* Use a *normal* mprotect(), not mprotect_pkey(): */
+       ret = mprotect(p1, PAGE_SIZE, PROT_EXEC);
+       pkey_assert(!ret);
+
+       dprintf2("pkru: %x\n", rdpkru());
+
+       /* Make sure this is an *instruction* fault */
+       madvise(p1, PAGE_SIZE, MADV_DONTNEED);
+       lots_o_noops_around_write(&scratch);
+       do_not_expect_pk_fault("executing on PROT_EXEC memory");
+       ptr_contents = read_ptr(p1);
+       dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents);
+       expected_pk_fault(UNKNOWN_PKEY);
+
+       /*
+        * Put the memory back to non-PROT_EXEC.  Should clear the
+        * exec-only pkey off the VMA and allow it to be readable
+        * again.  Go to PROT_NONE first to check for a kernel bug
+        * that did not clear the pkey when doing PROT_NONE.
+        */
+       ret = mprotect(p1, PAGE_SIZE, PROT_NONE);
+       pkey_assert(!ret);
+
+       ret = mprotect(p1, PAGE_SIZE, PROT_READ|PROT_EXEC);
+       pkey_assert(!ret);
+       ptr_contents = read_ptr(p1);
+       do_not_expect_pk_fault("plain read on recently PROT_EXEC area");
+}
+
 void test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey)
 {
        int size = PAGE_SIZE;
@@ -1302,6 +1406,8 @@ void (*pkey_tests[])(int *ptr, u16 pkey) = {
        test_kernel_gup_of_access_disabled_region,
        test_kernel_gup_write_to_write_disabled_region,
        test_executing_on_unreadable_memory,
+       test_implicit_mprotect_exec_only_memory,
+       test_mprotect_with_pkey_0,
        test_ptrace_of_child,
        test_pkey_syscalls_on_non_allocated_pkey,
        test_pkey_syscalls_bad_args,
index 10b3817..4ffc0b5 100644 (file)
@@ -211,6 +211,7 @@ static int vgic_debug_show(struct seq_file *s, void *v)
        struct vgic_state_iter *iter = (struct vgic_state_iter *)v;
        struct vgic_irq *irq;
        struct kvm_vcpu *vcpu = NULL;
+       unsigned long flags;
 
        if (iter->dist_id == 0) {
                print_dist_state(s, &kvm->arch.vgic);
@@ -227,9 +228,9 @@ static int vgic_debug_show(struct seq_file *s, void *v)
                irq = &kvm->arch.vgic.spis[iter->intid - VGIC_NR_PRIVATE_IRQS];
        }
 
-       spin_lock(&irq->irq_lock);
+       spin_lock_irqsave(&irq->irq_lock, flags);
        print_irq_state(s, irq, vcpu);
-       spin_unlock(&irq->irq_lock);
+       spin_unlock_irqrestore(&irq->irq_lock, flags);
 
        return 0;
 }
index a8f0724..4ed79c9 100644 (file)
@@ -52,6 +52,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
 {
        struct vgic_dist *dist = &kvm->arch.vgic;
        struct vgic_irq *irq = vgic_get_irq(kvm, NULL, intid), *oldirq;
+       unsigned long flags;
        int ret;
 
        /* In this case there is no put, since we keep the reference. */
@@ -71,7 +72,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
        irq->intid = intid;
        irq->target_vcpu = vcpu;
 
-       spin_lock(&dist->lpi_list_lock);
+       spin_lock_irqsave(&dist->lpi_list_lock, flags);
 
        /*
         * There could be a race with another vgic_add_lpi(), so we need to
@@ -99,7 +100,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
        dist->lpi_list_count++;
 
 out_unlock:
-       spin_unlock(&dist->lpi_list_lock);
+       spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
 
        /*
         * We "cache" the configuration table entries in our struct vgic_irq's.
@@ -280,8 +281,8 @@ static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq,
        int ret;
        unsigned long flags;
 
-       ret = kvm_read_guest(kvm, propbase + irq->intid - GIC_LPI_OFFSET,
-                            &prop, 1);
+       ret = kvm_read_guest_lock(kvm, propbase + irq->intid - GIC_LPI_OFFSET,
+                                 &prop, 1);
 
        if (ret)
                return ret;
@@ -315,6 +316,7 @@ static int vgic_copy_lpi_list(struct kvm_vcpu *vcpu, u32 **intid_ptr)
 {
        struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
        struct vgic_irq *irq;
+       unsigned long flags;
        u32 *intids;
        int irq_count, i = 0;
 
@@ -330,7 +332,7 @@ static int vgic_copy_lpi_list(struct kvm_vcpu *vcpu, u32 **intid_ptr)
        if (!intids)
                return -ENOMEM;
 
-       spin_lock(&dist->lpi_list_lock);
+       spin_lock_irqsave(&dist->lpi_list_lock, flags);
        list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
                if (i == irq_count)
                        break;
@@ -339,7 +341,7 @@ static int vgic_copy_lpi_list(struct kvm_vcpu *vcpu, u32 **intid_ptr)
                        continue;
                intids[i++] = irq->intid;
        }
-       spin_unlock(&dist->lpi_list_lock);
+       spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
 
        *intid_ptr = intids;
        return i;
@@ -348,10 +350,11 @@ static int vgic_copy_lpi_list(struct kvm_vcpu *vcpu, u32 **intid_ptr)
 static int update_affinity(struct vgic_irq *irq, struct kvm_vcpu *vcpu)
 {
        int ret = 0;
+       unsigned long flags;
 
-       spin_lock(&irq->irq_lock);
+       spin_lock_irqsave(&irq->irq_lock, flags);
        irq->target_vcpu = vcpu;
-       spin_unlock(&irq->irq_lock);
+       spin_unlock_irqrestore(&irq->irq_lock, flags);
 
        if (irq->hw) {
                struct its_vlpi_map map;
@@ -441,8 +444,9 @@ static int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu)
                 * this very same byte in the last iteration. Reuse that.
                 */
                if (byte_offset != last_byte_offset) {
-                       ret = kvm_read_guest(vcpu->kvm, pendbase + byte_offset,
-                                            &pendmask, 1);
+                       ret = kvm_read_guest_lock(vcpu->kvm,
+                                                 pendbase + byte_offset,
+                                                 &pendmask, 1);
                        if (ret) {
                                kfree(intids);
                                return ret;
@@ -786,7 +790,7 @@ static bool vgic_its_check_id(struct vgic_its *its, u64 baser, u32 id,
                return false;
 
        /* Each 1st level entry is represented by a 64-bit value. */
-       if (kvm_read_guest(its->dev->kvm,
+       if (kvm_read_guest_lock(its->dev->kvm,
                           BASER_ADDRESS(baser) + index * sizeof(indirect_ptr),
                           &indirect_ptr, sizeof(indirect_ptr)))
                return false;
@@ -1367,8 +1371,8 @@ static void vgic_its_process_commands(struct kvm *kvm, struct vgic_its *its)
        cbaser = CBASER_ADDRESS(its->cbaser);
 
        while (its->cwriter != its->creadr) {
-               int ret = kvm_read_guest(kvm, cbaser + its->creadr,
-                                        cmd_buf, ITS_CMD_SIZE);
+               int ret = kvm_read_guest_lock(kvm, cbaser + its->creadr,
+                                             cmd_buf, ITS_CMD_SIZE);
                /*
                 * If kvm_read_guest() fails, this could be due to the guest
                 * programming a bogus value in CBASER or something else going
@@ -1893,7 +1897,7 @@ static int scan_its_table(struct vgic_its *its, gpa_t base, int size, int esz,
                int next_offset;
                size_t byte_offset;
 
-               ret = kvm_read_guest(kvm, gpa, entry, esz);
+               ret = kvm_read_guest_lock(kvm, gpa, entry, esz);
                if (ret)
                        return ret;
 
@@ -2263,7 +2267,7 @@ static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz)
        int ret;
 
        BUG_ON(esz > sizeof(val));
-       ret = kvm_read_guest(kvm, gpa, &val, esz);
+       ret = kvm_read_guest_lock(kvm, gpa, &val, esz);
        if (ret)
                return ret;
        val = le64_to_cpu(val);
index c7423f3..bdcf8e7 100644 (file)
@@ -344,7 +344,7 @@ retry:
        bit_nr = irq->intid % BITS_PER_BYTE;
        ptr = pendbase + byte_offset;
 
-       ret = kvm_read_guest(kvm, ptr, &val, 1);
+       ret = kvm_read_guest_lock(kvm, ptr, &val, 1);
        if (ret)
                return ret;
 
@@ -397,7 +397,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
                ptr = pendbase + byte_offset;
 
                if (byte_offset != last_byte_offset) {
-                       ret = kvm_read_guest(kvm, ptr, &val, 1);
+                       ret = kvm_read_guest_lock(kvm, ptr, &val, 1);
                        if (ret)
                                return ret;
                        last_byte_offset = byte_offset;
index 97bfba8..33c8325 100644 (file)
@@ -43,9 +43,13 @@ struct vgic_global kvm_vgic_global_state __ro_after_init = {
  * kvm->lock (mutex)
  *   its->cmd_lock (mutex)
  *     its->its_lock (mutex)
- *       vgic_cpu->ap_list_lock
- *         kvm->lpi_list_lock
- *           vgic_irq->irq_lock
+ *       vgic_cpu->ap_list_lock                must be taken with IRQs disabled
+ *         kvm->lpi_list_lock          must be taken with IRQs disabled
+ *           vgic_irq->irq_lock                must be taken with IRQs disabled
+ *
+ * As the ap_list_lock might be taken from the timer interrupt handler,
+ * we have to disable IRQs before taking this lock and everything lower
+ * than it.
  *
  * If you need to take multiple locks, always take the upper lock first,
  * then the lower ones, e.g. first take the its_lock, then the irq_lock.
@@ -72,8 +76,9 @@ static struct vgic_irq *vgic_get_lpi(struct kvm *kvm, u32 intid)
 {
        struct vgic_dist *dist = &kvm->arch.vgic;
        struct vgic_irq *irq = NULL;
+       unsigned long flags;
 
-       spin_lock(&dist->lpi_list_lock);
+       spin_lock_irqsave(&dist->lpi_list_lock, flags);
 
        list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
                if (irq->intid != intid)
@@ -89,7 +94,7 @@ static struct vgic_irq *vgic_get_lpi(struct kvm *kvm, u32 intid)
        irq = NULL;
 
 out_unlock:
-       spin_unlock(&dist->lpi_list_lock);
+       spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
 
        return irq;
 }
@@ -134,19 +139,20 @@ static void vgic_irq_release(struct kref *ref)
 void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
 {
        struct vgic_dist *dist = &kvm->arch.vgic;
+       unsigned long flags;
 
        if (irq->intid < VGIC_MIN_LPI)
                return;
 
-       spin_lock(&dist->lpi_list_lock);
+       spin_lock_irqsave(&dist->lpi_list_lock, flags);
        if (!kref_put(&irq->refcount, vgic_irq_release)) {
-               spin_unlock(&dist->lpi_list_lock);
+               spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
                return;
        };
 
        list_del(&irq->lpi_list);
        dist->lpi_list_count--;
-       spin_unlock(&dist->lpi_list_lock);
+       spin_unlock_irqrestore(&dist->lpi_list_lock, flags);
 
        kfree(irq);
 }